Commit 11dd407e1870ed19774036df9da7747001413641
1 parent
a426cc2f
refactor: 移动端代码生成器列表页迁移为 z-paging
- 将 uniapp 列表模板切换为 z-paging 分页 - 支持返回列表自动刷新、顶部下拉刷新和底部自动加载更多 - 表单提交、详情删除后统一发送 reload 事件 - 详情页监听 reload 事件刷新数据,并增加删除中刷新守卫 - 新增 uniapp 代码生成器快照单测
Showing
24 changed files
with
1621 additions
and
77 deletions
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/components/search-form.vue.vm
| ... | ... | @@ -259,7 +259,7 @@ function handle${AttrName}1Confirm() { |
| 259 | 259 | #end |
| 260 | 260 | #end |
| 261 | 261 | |
| 262 | -/** 搜索 */ | |
| 262 | +/** 搜索按钮操作 */ | |
| 263 | 263 | function handleSearch() { |
| 264 | 264 | visible.value = false |
| 265 | 265 | emit('search', { |
| ... | ... | @@ -276,7 +276,7 @@ function handleSearch() { |
| 276 | 276 | }) |
| 277 | 277 | } |
| 278 | 278 | |
| 279 | -/** 重置 */ | |
| 279 | +/** 重置按钮操作 */ | |
| 280 | 280 | function handleReset() { |
| 281 | 281 | #foreach($column in $columns) |
| 282 | 282 | #if ($column.listOperation) | ... | ... |
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail/index.vue.vm
| ... | ... | @@ -62,6 +62,7 @@ |
| 62 | 62 | #end |
| 63 | 63 | #end |
| 64 | 64 | import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' |
| 65 | +import { onUnload } from '@dcloudio/uni-app' | |
| 65 | 66 | import { onMounted, ref } from 'vue' |
| 66 | 67 | import { useToast } from 'wot-design-uni' |
| 67 | 68 | import { delete${simpleClassName}, get${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' |
| ... | ... | @@ -97,7 +98,7 @@ function handleBack() { |
| 97 | 98 | |
| 98 | 99 | /** 加载${table.classComment}详情 */ |
| 99 | 100 | async function getDetail() { |
| 100 | - if (!props.id) { | |
| 101 | + if (!props.id || deleting.value) { | |
| 101 | 102 | return |
| 102 | 103 | } |
| 103 | 104 | try { |
| ... | ... | @@ -131,6 +132,7 @@ function handleDelete() { |
| 131 | 132 | try { |
| 132 | 133 | await delete${simpleClassName}(props.id) |
| 133 | 134 | toast.success('删除成功') |
| 135 | + uni.$emit('${table.moduleName}:${table.businessName}:reload') | |
| 134 | 136 | setTimeout(() => { |
| 135 | 137 | handleBack() |
| 136 | 138 | }, 500) |
| ... | ... | @@ -143,8 +145,14 @@ function handleDelete() { |
| 143 | 145 | |
| 144 | 146 | /** 初始化 */ |
| 145 | 147 | onMounted(() => { |
| 148 | + uni.$on('${table.moduleName}:${table.businessName}:reload', getDetail) | |
| 146 | 149 | getDetail() |
| 147 | 150 | }) |
| 151 | + | |
| 152 | +/** 卸载 */ | |
| 153 | +onUnload(() => { | |
| 154 | + uni.$off('${table.moduleName}:${table.businessName}:reload', getDetail) | |
| 155 | +}) | |
| 148 | 156 | </script> |
| 149 | 157 | |
| 150 | 158 | <style lang="scss" scoped> | ... | ... |
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/form/index.vue.vm
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/index.vue.vm
| 1 | 1 | <template> |
| 2 | - <view class="yd-page-container"> | |
| 2 | + <view class="yd-page-container yd-page-container-paging"> | |
| 3 | 3 | <!-- 顶部导航栏 --> |
| 4 | 4 | <wd-navbar |
| 5 | 5 | title="${table.classComment}管理" |
| ... | ... | @@ -11,14 +11,26 @@ |
| 11 | 11 | <SearchForm @search="handleQuery" @reset="handleReset" /> |
| 12 | 12 | |
| 13 | 13 | <!-- ${table.classComment}列表 --> |
| 14 | - <view class="p-24rpx"> | |
| 15 | - <view | |
| 16 | - v-for="item in list" | |
| 17 | - :key="item.${primaryColumn.javaField}" | |
| 18 | - class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm" | |
| 19 | - @click="handleDetail(item)" | |
| 20 | - > | |
| 21 | - <view class="p-24rpx"> | |
| 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="暂无${table.classComment}数据" | |
| 24 | + @query="queryList" | |
| 25 | + > | |
| 26 | + <view class="p-24rpx"> | |
| 27 | + <view | |
| 28 | + v-for="item in list" | |
| 29 | + :key="item.${primaryColumn.javaField}" | |
| 30 | + class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm" | |
| 31 | + @click="handleDetail(item)" | |
| 32 | + > | |
| 33 | + <view class="p-24rpx"> | |
| 22 | 34 | #set ($titleField = "") |
| 23 | 35 | #set ($statusField = "") |
| 24 | 36 | #set ($statusDictType = "") |
| ... | ... | @@ -35,14 +47,14 @@ |
| 35 | 47 | #if ($titleField == "") |
| 36 | 48 | #set ($titleField = $primaryColumn.javaField) |
| 37 | 49 | #end |
| 38 | - <view class="mb-16rpx flex items-center justify-between"> | |
| 39 | - <view class="text-32rpx text-[#333] font-semibold"> | |
| 40 | - {{ item.${titleField} }} | |
| 41 | - </view> | |
| 50 | + <view class="mb-16rpx flex items-center justify-between"> | |
| 51 | + <view class="text-32rpx text-[#333] font-semibold"> | |
| 52 | + {{ item.${titleField} }} | |
| 53 | + </view> | |
| 42 | 54 | #if($statusField != "") |
| 43 | - <dict-tag :type="DICT_TYPE.${statusDictType.toUpperCase()}" :value="item.${statusField}" /> | |
| 55 | + <dict-tag :type="DICT_TYPE.${statusDictType.toUpperCase()}" :value="item.${statusField}" /> | |
| 44 | 56 | #end |
| 45 | - </view> | |
| 57 | + </view> | |
| 46 | 58 | #foreach($column in $columns) |
| 47 | 59 | #if ($column.listOperationResult && !$column.primaryKey && $column.javaField != $titleField && $column.javaField != $statusField) |
| 48 | 60 | #set ($javaField = $column.javaField) |
| ... | ... | @@ -50,36 +62,27 @@ |
| 50 | 62 | #set ($dictType = $column.dictType) |
| 51 | 63 | #set ($javaType = $column.javaType) |
| 52 | 64 | #if ($dictType && "" != $dictType) |
| 53 | - <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 54 | - <text class="mr-8rpx text-[#999]">${comment}:</text> | |
| 55 | - <dict-tag :type="DICT_TYPE.${dictType.toUpperCase()}" :value="item.${javaField}" /> | |
| 56 | - </view> | |
| 65 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 66 | + <text class="mr-8rpx text-[#999]">${comment}:</text> | |
| 67 | + <dict-tag :type="DICT_TYPE.${dictType.toUpperCase()}" :value="item.${javaField}" /> | |
| 68 | + </view> | |
| 57 | 69 | #elseif ($javaType == "LocalDateTime") |
| 58 | - <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 59 | - <text class="mr-8rpx text-[#999]">${comment}:</text> | |
| 60 | - <text class="line-clamp-1">{{ formatDateTime(item.${javaField}) || '-' }}</text> | |
| 61 | - </view> | |
| 70 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 71 | + <text class="mr-8rpx text-[#999]">${comment}:</text> | |
| 72 | + <text class="line-clamp-1">{{ formatDateTime(item.${javaField}) || '-' }}</text> | |
| 73 | + </view> | |
| 62 | 74 | #else |
| 63 | - <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 64 | - <text class="mr-8rpx text-[#999]">${comment}:</text> | |
| 65 | - <text class="line-clamp-1">{{ item.${javaField} }}</text> | |
| 66 | - </view> | |
| 75 | + <view class="mb-12rpx flex items-center text-28rpx text-[#666]"> | |
| 76 | + <text class="mr-8rpx text-[#999]">${comment}:</text> | |
| 77 | + <text class="line-clamp-1">{{ item.${javaField} }}</text> | |
| 78 | + </view> | |
| 67 | 79 | #end |
| 68 | 80 | #end |
| 69 | 81 | #end |
| 82 | + </view> | |
| 70 | 83 | </view> |
| 71 | 84 | </view> |
| 72 | - | |
| 73 | - <!-- 加载更多 --> | |
| 74 | - <view v-if="loadMoreState !== 'loading' && list.length === 0" class="py-100rpx text-center"> | |
| 75 | - <wd-status-tip image="content" tip="暂无${table.classComment}数据" /> | |
| 76 | - </view> | |
| 77 | - <wd-loadmore | |
| 78 | - v-if="list.length > 0" | |
| 79 | - :state="loadMoreState" | |
| 80 | - @reload="loadMore" | |
| 81 | - /> | |
| 82 | - </view> | |
| 85 | + </z-paging> | |
| 83 | 86 | |
| 84 | 87 | <!-- 新增按钮 --> |
| 85 | 88 | <wd-fab |
| ... | ... | @@ -108,8 +111,7 @@ |
| 108 | 111 | #end |
| 109 | 112 | #end |
| 110 | 113 | import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}' |
| 111 | -import type { LoadMoreState } from '@/http/types' | |
| 112 | -import { onReachBottom } from '@dcloudio/uni-app' | |
| 114 | +import { onUnload } from '@dcloudio/uni-app' | |
| 113 | 115 | import { onMounted, ref } from 'vue' |
| 114 | 116 | import { get${simpleClassName}Page } from '@/api/${table.moduleName}/${table.businessName}' |
| 115 | 117 | import { useAccess } from '@/hooks/useAccess' |
| ... | ... | @@ -130,13 +132,9 @@ definePage({ |
| 130 | 132 | }) |
| 131 | 133 | |
| 132 | 134 | const { hasAccessByCodes } = useAccess() |
| 133 | -const total = ref(0) // 列表总数 | |
| 134 | 135 | const list = ref<${simpleClassName}[]>([]) // 列表数据 |
| 135 | -const loadMoreState = ref<LoadMoreState>('loading') // 分页加载状态 | |
| 136 | -const queryParams = ref({ | |
| 137 | - pageNo: 1, | |
| 138 | - pageSize: 10, | |
| 139 | -}) // 查询参数 | |
| 136 | +const pagingRef = ref<any>() // 分页组件引用 | |
| 137 | +const queryParams = ref<Record<string, any>>({}) // 查询参数 | |
| 140 | 138 | |
| 141 | 139 | /** 返回上一页 */ |
| 142 | 140 | function handleBack() { |
| ... | ... | @@ -144,28 +142,24 @@ function handleBack() { |
| 144 | 142 | } |
| 145 | 143 | |
| 146 | 144 | /** 查询${table.classComment}列表 */ |
| 147 | -async function getList() { | |
| 148 | - loadMoreState.value = 'loading' | |
| 145 | +async function queryList(pageNo: number, pageSize: number) { | |
| 149 | 146 | try { |
| 150 | - const data = await get${simpleClassName}Page(queryParams.value) | |
| 151 | - list.value = [...list.value, ...data.list] | |
| 152 | - total.value = data.total | |
| 153 | - loadMoreState.value = list.value.length >= total.value ? 'finished' : 'loading' | |
| 147 | + const params = { | |
| 148 | + ...queryParams.value, | |
| 149 | + pageNo, | |
| 150 | + pageSize, | |
| 151 | + } | |
| 152 | + const data = await get${simpleClassName}Page(params) | |
| 153 | + pagingRef.value?.completeByTotal(data.list, data.total) | |
| 154 | 154 | } catch { |
| 155 | - queryParams.value.pageNo = queryParams.value.pageNo > 1 ? queryParams.value.pageNo - 1 : 1 | |
| 156 | - loadMoreState.value = 'error' | |
| 155 | + pagingRef.value?.complete(false) | |
| 157 | 156 | } |
| 158 | 157 | } |
| 159 | 158 | |
| 160 | 159 | /** 搜索按钮操作 */ |
| 161 | 160 | function handleQuery(data?: Record<string, any>) { |
| 162 | - queryParams.value = { | |
| 163 | - ...data, | |
| 164 | - pageNo: 1, | |
| 165 | - pageSize: queryParams.value.pageSize, | |
| 166 | - } | |
| 167 | - list.value = [] | |
| 168 | - getList() | |
| 161 | + queryParams.value = { ...data } | |
| 162 | + reload() | |
| 169 | 163 | } |
| 170 | 164 | |
| 171 | 165 | /** 重置按钮操作 */ |
| ... | ... | @@ -173,13 +167,9 @@ function handleReset() { |
| 173 | 167 | handleQuery() |
| 174 | 168 | } |
| 175 | 169 | |
| 176 | -/** 加载更多 */ | |
| 177 | -function loadMore() { | |
| 178 | - if (loadMoreState.value === 'finished') { | |
| 179 | - return | |
| 180 | - } | |
| 181 | - queryParams.value.pageNo++ | |
| 182 | - getList() | |
| 170 | +/** 重新加载 */ | |
| 171 | +function reload() { | |
| 172 | + pagingRef.value?.reload() | |
| 183 | 173 | } |
| 184 | 174 | |
| 185 | 175 | /** 新增${table.classComment} */ |
| ... | ... | @@ -196,14 +186,14 @@ function handleDetail(item: ${simpleClassName}) { |
| 196 | 186 | }) |
| 197 | 187 | } |
| 198 | 188 | |
| 199 | -/** 触底加载更多 */ | |
| 200 | -onReachBottom(() => { | |
| 201 | - loadMore() | |
| 202 | -}) | |
| 203 | - | |
| 204 | 189 | /** 初始化 */ |
| 205 | 190 | onMounted(() => { |
| 206 | - getList() | |
| 191 | + uni.$on('${table.moduleName}:${table.businessName}:reload', reload) | |
| 192 | +}) | |
| 193 | + | |
| 194 | +/** 卸载 */ | |
| 195 | +onUnload(() => { | |
| 196 | + uni.$off('${table.moduleName}:${table.businessName}:reload', reload) | |
| 207 | 197 | }) |
| 208 | 198 | </script> |
| 209 | 199 | ... | ... |
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineUniappTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.service.codegen.inner; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; | |
| 4 | +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; | |
| 5 | +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; | |
| 6 | +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; | |
| 7 | +import com.baomidou.mybatisplus.annotation.DbType; | |
| 8 | +import org.junit.jupiter.api.Test; | |
| 9 | + | |
| 10 | +import java.util.List; | |
| 11 | +import java.util.Map; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * {@link CodegenEngine} 的 Vue3 Admin Uniapp + Wot UI 单元测试 | |
| 15 | + * | |
| 16 | + * @author 芋道源码 | |
| 17 | + */ | |
| 18 | +public class CodegenEngineUniappTest extends CodegenEngineAbstractTest { | |
| 19 | + | |
| 20 | + @Test | |
| 21 | + public void testExecute_one() { | |
| 22 | + // 准备参数 | |
| 23 | + CodegenTableDO table = getTable("student") | |
| 24 | + .setFrontType(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType()) | |
| 25 | + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType()); | |
| 26 | + List<CodegenColumnDO> columns = getColumnList("student"); | |
| 27 | + | |
| 28 | + // 调用 | |
| 29 | + Map<String, String> result = codegenEngine.execute(DbType.MYSQL, table, columns, null, null); | |
| 30 | + // 断言 | |
| 31 | + assertResult(result, "/vue3_admin_uniapp_one"); | |
| 32 | + } | |
| 33 | + | |
| 34 | +} | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/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/InfraStudentMapper", | |
| 18 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" | |
| 19 | +}, { | |
| 20 | + "contentPath" : "xml/InfraStudentMapper", | |
| 21 | + "filePath" : "yudao-module-infra/src/main/resources/mapper/demo/InfraStudentMapper.xml" | |
| 22 | +}, { | |
| 23 | + "contentPath" : "java/InfraStudentServiceImpl", | |
| 24 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" | |
| 25 | +}, { | |
| 26 | + "contentPath" : "java/InfraStudentService", | |
| 27 | + "filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" | |
| 28 | +}, { | |
| 29 | + "contentPath" : "java/InfraStudentServiceImplTest", | |
| 30 | + "filePath" : "yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.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/index", | |
| 45 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/index.vue" | |
| 46 | +}, { | |
| 47 | + "contentPath" : "vue/search-form", | |
| 48 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/components/search-form.vue" | |
| 49 | +}, { | |
| 50 | + "contentPath" : "vue/form/index", | |
| 51 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue" | |
| 52 | +}, { | |
| 53 | + "contentPath" : "vue/detail/index", | |
| 54 | + "filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue" | |
| 55 | +} ] | |
| 0 | 56 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/ErrorCodeConstants_手动操作
0 → 100644
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentController
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.controller.admin.demo; | |
| 2 | + | |
| 3 | +import org.springframework.web.bind.annotation.*; | |
| 4 | +import 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.service.demo.InfraStudentService; | |
| 31 | + | |
| 32 | +@Tag(name = "管理后台 - 学生") | |
| 33 | +@RestController | |
| 34 | +@RequestMapping("/infra/student") | |
| 35 | +@Validated | |
| 36 | +public class InfraStudentController { | |
| 37 | + | |
| 38 | + @Resource | |
| 39 | + private InfraStudentService studentService; | |
| 40 | + | |
| 41 | + @PostMapping("/create") | |
| 42 | + @Operation(summary = "创建学生") | |
| 43 | + @PreAuthorize("@ss.hasPermission('infra:student:create')") | |
| 44 | + public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { | |
| 45 | + return success(studentService.createStudent(createReqVO)); | |
| 46 | + } | |
| 47 | + | |
| 48 | + @PutMapping("/update") | |
| 49 | + @Operation(summary = "更新学生") | |
| 50 | + @PreAuthorize("@ss.hasPermission('infra:student:update')") | |
| 51 | + public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { | |
| 52 | + studentService.updateStudent(updateReqVO); | |
| 53 | + return success(true); | |
| 54 | + } | |
| 55 | + | |
| 56 | + @DeleteMapping("/delete") | |
| 57 | + @Operation(summary = "删除学生") | |
| 58 | + @Parameter(name = "id", description = "编号", required = true) | |
| 59 | + @PreAuthorize("@ss.hasPermission('infra:student:delete')") | |
| 60 | + public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) { | |
| 61 | + studentService.deleteStudent(id); | |
| 62 | + return success(true); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @DeleteMapping("/delete-list") | |
| 66 | + @Parameter(name = "ids", description = "编号", required = true) | |
| 67 | + @Operation(summary = "批量删除学生") | |
| 68 | + @PreAuthorize("@ss.hasPermission('infra:student:delete')") | |
| 69 | + public CommonResult<Boolean> deleteStudentList(@RequestParam("ids") List<Long> ids) { | |
| 70 | + studentService.deleteStudentListByIds(ids); | |
| 71 | + return success(true); | |
| 72 | + } | |
| 73 | + | |
| 74 | + @GetMapping("/get") | |
| 75 | + @Operation(summary = "获得学生") | |
| 76 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 77 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 78 | + public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) { | |
| 79 | + InfraStudentDO student = studentService.getStudent(id); | |
| 80 | + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); | |
| 81 | + } | |
| 82 | + | |
| 83 | + @GetMapping("/page") | |
| 84 | + @Operation(summary = "获得学生分页") | |
| 85 | + @PreAuthorize("@ss.hasPermission('infra:student:query')") | |
| 86 | + public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { | |
| 87 | + PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO); | |
| 88 | + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); | |
| 89 | + } | |
| 90 | + | |
| 91 | + @GetMapping("/export-excel") | |
| 92 | + @Operation(summary = "导出学生 Excel") | |
| 93 | + @PreAuthorize("@ss.hasPermission('infra:student:export')") | |
| 94 | + @ApiAccessLog(operateType = EXPORT) | |
| 95 | + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, | |
| 96 | + HttpServletResponse response) throws IOException { | |
| 97 | + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 98 | + List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList(); | |
| 99 | + // 导出 Excel | |
| 100 | + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, | |
| 101 | + BeanUtils.toBean(list, InfraStudentRespVO.class)); | |
| 102 | + } | |
| 103 | + | |
| 104 | +} | |
| 0 | 105 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentDO
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; | |
| 2 | + | |
| 3 | +import lombok.*; | |
| 4 | +import java.util.*; | |
| 5 | +import java.time.LocalDateTime; | |
| 6 | +import java.time.LocalDateTime; | |
| 7 | +import com.baomidou.mybatisplus.annotation.*; | |
| 8 | +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 学生 DO | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +@TableName("infra_student") | |
| 16 | +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | |
| 17 | +@Data | |
| 18 | +@EqualsAndHashCode(callSuper = true) | |
| 19 | +@ToString(callSuper = true) | |
| 20 | +@Builder | |
| 21 | +@NoArgsConstructor | |
| 22 | +@AllArgsConstructor | |
| 23 | +public class InfraStudentDO extends BaseDO { | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 编号 | |
| 27 | + */ | |
| 28 | + @TableId | |
| 29 | + private Long id; | |
| 30 | + /** | |
| 31 | + * 名字 | |
| 32 | + */ | |
| 33 | + private String name; | |
| 34 | + /** | |
| 35 | + * 简介 | |
| 36 | + */ | |
| 37 | + private String description; | |
| 38 | + /** | |
| 39 | + * 出生日期 | |
| 40 | + */ | |
| 41 | + private LocalDateTime birthday; | |
| 42 | + /** | |
| 43 | + * 性别 | |
| 44 | + * | |
| 45 | + * 枚举 {@link TODO system_user_sex 对应的类} | |
| 46 | + */ | |
| 47 | + private Integer sex; | |
| 48 | + /** | |
| 49 | + * 是否有效 | |
| 50 | + * | |
| 51 | + * 枚举 {@link TODO infra_boolean_string 对应的类} | |
| 52 | + */ | |
| 53 | + private Boolean enabled; | |
| 54 | + /** | |
| 55 | + * 头像 | |
| 56 | + */ | |
| 57 | + private String avatar; | |
| 58 | + /** | |
| 59 | + * 附件 | |
| 60 | + */ | |
| 61 | + private String video; | |
| 62 | + /** | |
| 63 | + * 备注 | |
| 64 | + */ | |
| 65 | + private String memo; | |
| 66 | + | |
| 67 | + | |
| 68 | +} | |
| 0 | 69 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentMapper
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.dal.mysql.demo; | |
| 2 | + | |
| 3 | +import java.util.*; | |
| 4 | + | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 6 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | |
| 7 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | |
| 8 | +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; | |
| 9 | +import org.apache.ibatis.annotations.Mapper; | |
| 10 | +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * 学生 Mapper | |
| 14 | + * | |
| 15 | + * @author 芋道源码 | |
| 16 | + */ | |
| 17 | +@Mapper | |
| 18 | +public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> { | |
| 19 | + | |
| 20 | + default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) { | |
| 21 | + return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>() | |
| 22 | + .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) | |
| 23 | + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) | |
| 24 | + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) | |
| 25 | + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) | |
| 26 | + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) | |
| 27 | + .orderByDesc(InfraStudentDO::getId)); | |
| 28 | + } | |
| 29 | + | |
| 30 | +} | |
| 0 | 31 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentPageReqVO
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; | |
| 2 | + | |
| 3 | +import lombok.*; | |
| 4 | +import java.util.*; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 7 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | + | |
| 10 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - 学生分页 Request VO") | |
| 13 | +@Data | |
| 14 | +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_one/java/InfraStudentRespVO
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.*; | |
| 5 | +import java.util.*; | |
| 6 | +import 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_one/java/InfraStudentSaveReqVO
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.*; | |
| 5 | +import java.util.*; | |
| 6 | +import jakarta.validation.constraints.*; | |
| 7 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | + | |
| 10 | +@Schema(description = "管理后台 - 学生新增/修改 Request VO") | |
| 11 | +@Data | |
| 12 | +public class InfraStudentSaveReqVO { | |
| 13 | + | |
| 14 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") | |
| 18 | + @NotEmpty(message = "名字不能为空") | |
| 19 | + private String name; | |
| 20 | + | |
| 21 | + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") | |
| 22 | + @NotEmpty(message = "简介不能为空") | |
| 23 | + private String description; | |
| 24 | + | |
| 25 | + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 26 | + @NotNull(message = "出生日期不能为空") | |
| 27 | + private LocalDateTime birthday; | |
| 28 | + | |
| 29 | + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 30 | + @NotNull(message = "性别不能为空") | |
| 31 | + private Integer sex; | |
| 32 | + | |
| 33 | + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | |
| 34 | + @NotNull(message = "是否有效不能为空") | |
| 35 | + private Boolean enabled; | |
| 36 | + | |
| 37 | + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") | |
| 38 | + @NotEmpty(message = "头像不能为空") | |
| 39 | + private String avatar; | |
| 40 | + | |
| 41 | + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") | |
| 42 | + @NotEmpty(message = "附件不能为空") | |
| 43 | + private String video; | |
| 44 | + | |
| 45 | + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") | |
| 46 | + @NotEmpty(message = "备注不能为空") | |
| 47 | + private String memo; | |
| 48 | + | |
| 49 | +} | |
| 0 | 50 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/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.framework.common.pojo.PageResult; | |
| 8 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 学生 Service 接口 | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +public interface InfraStudentService { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 创建学生 | |
| 19 | + * | |
| 20 | + * @param createReqVO 创建信息 | |
| 21 | + * @return 编号 | |
| 22 | + */ | |
| 23 | + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 更新学生 | |
| 27 | + * | |
| 28 | + * @param updateReqVO 更新信息 | |
| 29 | + */ | |
| 30 | + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); | |
| 31 | + | |
| 32 | + /** | |
| 33 | + * 删除学生 | |
| 34 | + * | |
| 35 | + * @param id 编号 | |
| 36 | + */ | |
| 37 | + void deleteStudent(Long id); | |
| 38 | + | |
| 39 | + /** | |
| 40 | + * 批量删除学生 | |
| 41 | + * | |
| 42 | + * @param ids 编号 | |
| 43 | + */ | |
| 44 | + void deleteStudentListByIds(List<Long> ids); | |
| 45 | + | |
| 46 | + /** | |
| 47 | + * 获得学生 | |
| 48 | + * | |
| 49 | + * @param id 编号 | |
| 50 | + * @return 学生 | |
| 51 | + */ | |
| 52 | + InfraStudentDO getStudent(Long id); | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * 获得学生分页 | |
| 56 | + * | |
| 57 | + * @param pageReqVO 分页查询 | |
| 58 | + * @return 学生分页 | |
| 59 | + */ | |
| 60 | + PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO); | |
| 61 | + | |
| 62 | +} | |
| 0 | 63 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/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.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.InfraStudentMapper; | |
| 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 InfraStudentServiceImpl implements InfraStudentService { | |
| 31 | + | |
| 32 | + @Resource | |
| 33 | + private InfraStudentMapper studentMapper; | |
| 34 | + | |
| 35 | + @Override | |
| 36 | + public Long createStudent(InfraStudentSaveReqVO createReqVO) { | |
| 37 | + // 插入 | |
| 38 | + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); | |
| 39 | + studentMapper.insert(student); | |
| 40 | + | |
| 41 | + // 返回 | |
| 42 | + return student.getId(); | |
| 43 | + } | |
| 44 | + | |
| 45 | + @Override | |
| 46 | + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { | |
| 47 | + // 校验存在 | |
| 48 | + validateStudentExists(updateReqVO.getId()); | |
| 49 | + // 更新 | |
| 50 | + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); | |
| 51 | + studentMapper.updateById(updateObj); | |
| 52 | + } | |
| 53 | + | |
| 54 | + @Override | |
| 55 | + public void deleteStudent(Long id) { | |
| 56 | + // 校验存在 | |
| 57 | + validateStudentExists(id); | |
| 58 | + // 删除 | |
| 59 | + studentMapper.deleteById(id); | |
| 60 | + } | |
| 61 | + | |
| 62 | + @Override | |
| 63 | + public void deleteStudentListByIds(List<Long> ids) { | |
| 64 | + // 删除 | |
| 65 | + studentMapper.deleteByIds(ids); | |
| 66 | + } | |
| 67 | + | |
| 68 | + | |
| 69 | + private void validateStudentExists(Long id) { | |
| 70 | + if (studentMapper.selectById(id) == null) { | |
| 71 | + throw exception(STUDENT_NOT_EXISTS); | |
| 72 | + } | |
| 73 | + } | |
| 74 | + | |
| 75 | + @Override | |
| 76 | + public InfraStudentDO getStudent(Long id) { | |
| 77 | + return studentMapper.selectById(id); | |
| 78 | + } | |
| 79 | + | |
| 80 | + @Override | |
| 81 | + public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) { | |
| 82 | + return studentMapper.selectPage(pageReqVO); | |
| 83 | + } | |
| 84 | + | |
| 85 | +} | |
| 0 | 86 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentServiceImplTest
0 → 100644
| 1 | +package cn.iocoder.yudao.module.infra.service.demo; | |
| 2 | + | |
| 3 | +import org.junit.jupiter.api.Disabled; | |
| 4 | +import org.junit.jupiter.api.Test; | |
| 5 | + | |
| 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_one/sql/h2
0 → 100644
| 1 | +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 | |
| 2 | +CREATE TABLE IF NOT EXISTS "infra_student" ( | |
| 3 | + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | |
| 4 | + "name" varchar NOT NULL, | |
| 5 | + "description" varchar NOT NULL, | |
| 6 | + "birthday" varchar NOT NULL, | |
| 7 | + "sex" int NOT NULL, | |
| 8 | + "enabled" bit NOT NULL, | |
| 9 | + "avatar" varchar NOT NULL, | |
| 10 | + "video" varchar NOT NULL, | |
| 11 | + "memo" varchar NOT NULL, | |
| 12 | + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 13 | + PRIMARY KEY ("id") | |
| 14 | +) COMMENT '学生表'; | |
| 15 | + | |
| 16 | +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 | |
| 17 | +DELETE FROM "infra_student"; | |
| 0 | 18 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/sql/sql
0 → 100644
| 1 | +-- 菜单 SQL | |
| 2 | +INSERT INTO system_menu( | |
| 3 | + name, permission, type, sort, parent_id, | |
| 4 | + path, icon, component, status, component_name | |
| 5 | +) | |
| 6 | +VALUES ( | |
| 7 | + '学生管理', '', 2, 0, 888, | |
| 8 | + 'student', '', 'infra/demo/index', 0, 'InfraStudent' | |
| 9 | +); | |
| 10 | + | |
| 11 | +-- 按钮父菜单ID | |
| 12 | +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_one/ts/index
0 → 100644
| 1 | +import type { PageParam, PageResult } from '@/http/types' | |
| 2 | +import { http } from '@/http/http' | |
| 3 | + | |
| 4 | +/** 学生信息 */ | |
| 5 | +export interface Student { | |
| 6 | + id?: number | |
| 7 | + name: string | |
| 8 | + description: string | |
| 9 | + birthday: Date | |
| 10 | + sex: number | |
| 11 | + enabled: boolean | |
| 12 | + avatar: string | |
| 13 | + video: string | |
| 14 | + memo: string | |
| 15 | + createTime?: Date | |
| 16 | +} | |
| 17 | + | |
| 18 | +/** 获取学生分页列表 */ | |
| 19 | +export function getStudentPage(params: PageParam) { | |
| 20 | + return http.get<PageResult<Student>>('/infra/student/page', params) | |
| 21 | +} | |
| 22 | + | |
| 23 | +/** 获取学生详情 */ | |
| 24 | +export function getStudent(id: number) { | |
| 25 | + return http.get<Student>(`/infra/student/get?id=${id}`) | |
| 26 | +} | |
| 27 | + | |
| 28 | +/** 创建学生 */ | |
| 29 | +export function createStudent(data: Student) { | |
| 30 | + return http.post<number>('/infra/student/create', data) | |
| 31 | +} | |
| 32 | + | |
| 33 | +/** 更新学生 */ | |
| 34 | +export function updateStudent(data: Student) { | |
| 35 | + return http.put<boolean>('/infra/student/update', data) | |
| 36 | +} | |
| 37 | + | |
| 38 | +/** 删除学生 */ | |
| 39 | +export function deleteStudent(id: number) { | |
| 40 | + return http.delete<boolean>(`/infra/student/delete?id=${id}`) | |
| 41 | +} | |
| 0 | 42 | \ No newline at end of file | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/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?.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 | + </view> | |
| 29 | + | |
| 30 | + <!-- 底部操作按钮 --> | |
| 31 | + <view class="yd-detail-footer"> | |
| 32 | + <view class="yd-detail-footer-actions"> | |
| 33 | + <wd-button | |
| 34 | + v-if="hasAccessByCodes(['infra:student:update'])" | |
| 35 | + class="flex-1" type="warning" @click="handleEdit" | |
| 36 | + > | |
| 37 | + 编辑 | |
| 38 | + </wd-button> | |
| 39 | + <wd-button | |
| 40 | + v-if="hasAccessByCodes(['infra:student:delete'])" | |
| 41 | + class="flex-1" type="error" :loading="deleting" @click="handleDelete" | |
| 42 | + > | |
| 43 | + 删除 | |
| 44 | + </wd-button> | |
| 45 | + </view> | |
| 46 | + </view> | |
| 47 | + </view> | |
| 48 | +</template> | |
| 49 | + | |
| 50 | +<script lang="ts" setup> | |
| 51 | +import type { Student } from '@/api/infra/demo' | |
| 52 | +import { onUnload } from '@dcloudio/uni-app' | |
| 53 | +import { onMounted, ref } from 'vue' | |
| 54 | +import { useToast } from 'wot-design-uni' | |
| 55 | +import { deleteStudent, getStudent } from '@/api/infra/demo' | |
| 56 | +import { useAccess } from '@/hooks/useAccess' | |
| 57 | +import { navigateBackPlus } from '@/utils' | |
| 58 | +import { DICT_TYPE } from '@/utils/constants' | |
| 59 | +import { formatDateTime } from '@/utils/date' | |
| 60 | + | |
| 61 | +const props = defineProps<{ | |
| 62 | + id?: number | any | |
| 63 | +}>() | |
| 64 | + | |
| 65 | +definePage({ | |
| 66 | + style: { | |
| 67 | + navigationBarTitleText: '', | |
| 68 | + navigationStyle: 'custom', | |
| 69 | + }, | |
| 70 | +}) | |
| 71 | + | |
| 72 | +const { hasAccessByCodes } = useAccess() | |
| 73 | +const toast = useToast() | |
| 74 | +const formData = ref<Student>() // 详情数据 | |
| 75 | +const deleting = ref(false) // 删除状态 | |
| 76 | + | |
| 77 | +/** 返回上一页 */ | |
| 78 | +function handleBack() { | |
| 79 | + navigateBackPlus('/pages-infra/demo/index') | |
| 80 | +} | |
| 81 | + | |
| 82 | +/** 加载学生详情 */ | |
| 83 | +async function getDetail() { | |
| 84 | + if (!props.id || deleting.value) { | |
| 85 | + return | |
| 86 | + } | |
| 87 | + try { | |
| 88 | + toast.loading('加载中...') | |
| 89 | + formData.value = await getStudent(props.id) | |
| 90 | + } finally { | |
| 91 | + toast.close() | |
| 92 | + } | |
| 93 | +} | |
| 94 | + | |
| 95 | +/** 编辑学生 */ | |
| 96 | +function handleEdit() { | |
| 97 | + uni.navigateTo({ | |
| 98 | + url: `/pages-infra/demo/form/index?id=${props.id}`, | |
| 99 | + }) | |
| 100 | +} | |
| 101 | + | |
| 102 | +/** 删除学生 */ | |
| 103 | +function handleDelete() { | |
| 104 | + if (!props.id) { | |
| 105 | + return | |
| 106 | + } | |
| 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 | + setTimeout(() => { | |
| 120 | + handleBack() | |
| 121 | + }, 500) | |
| 122 | + } finally { | |
| 123 | + deleting.value = false | |
| 124 | + } | |
| 125 | + }, | |
| 126 | + }) | |
| 127 | +} | |
| 128 | + | |
| 129 | +/** 初始化 */ | |
| 130 | +onMounted(() => { | |
| 131 | + uni.$on('infra:demo:reload', getDetail) | |
| 132 | + getDetail() | |
| 133 | +}) | |
| 134 | + | |
| 135 | +/** 卸载 */ | |
| 136 | +onUnload(() => { | |
| 137 | + uni.$off('infra:demo:reload', getDetail) | |
| 138 | +}) | |
| 139 | +</script> | |
| 140 | + | |
| 141 | +<style lang="scss" scoped> | |
| 142 | +</style> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/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" :rules="formRules"> | |
| 13 | + <wd-cell-group border> | |
| 14 | + <wd-input | |
| 15 | + v-model="formData.name" | |
| 16 | + label="名字" | |
| 17 | + label-width="180rpx" | |
| 18 | + prop="name" | |
| 19 | + clearable | |
| 20 | + placeholder="请输入名字" | |
| 21 | + /> | |
| 22 | + <wd-textarea | |
| 23 | + v-model="formData.description" | |
| 24 | + label="简介" | |
| 25 | + label-width="180rpx" | |
| 26 | + placeholder="请输入简介" | |
| 27 | + :maxlength="200" | |
| 28 | + show-word-limit | |
| 29 | + clearable | |
| 30 | + /> | |
| 31 | + <wd-datetime-picker | |
| 32 | + v-model="formData.birthday" | |
| 33 | + type="datetime" | |
| 34 | + label="出生日期" | |
| 35 | + label-width="180rpx" | |
| 36 | + prop="birthday" | |
| 37 | + /> | |
| 38 | + <wd-cell title="性别" title-width="180rpx" prop="sex" center> | |
| 39 | + <wd-radio-group v-model="formData.sex" shape="button"> | |
| 40 | + <wd-radio | |
| 41 | + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" | |
| 42 | + :key="dict.value" | |
| 43 | + :value="dict.value" | |
| 44 | + > | |
| 45 | + {{ dict.label }} | |
| 46 | + </wd-radio> | |
| 47 | + </wd-radio-group> | |
| 48 | + </wd-cell> | |
| 49 | + <wd-cell title="是否有效" title-width="180rpx" prop="enabled" center> | |
| 50 | + <wd-radio-group v-model="formData.enabled" shape="button"> | |
| 51 | + <wd-radio | |
| 52 | + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" | |
| 53 | + :key="dict.value" | |
| 54 | + :value="dict.value" | |
| 55 | + > | |
| 56 | + {{ dict.label }} | |
| 57 | + </wd-radio> | |
| 58 | + </wd-radio-group> | |
| 59 | + </wd-cell> | |
| 60 | + <wd-input | |
| 61 | + v-model="formData.avatar" | |
| 62 | + label="头像" | |
| 63 | + label-width="180rpx" | |
| 64 | + prop="avatar" | |
| 65 | + clearable | |
| 66 | + placeholder="请输入头像" | |
| 67 | + /> | |
| 68 | + <wd-input | |
| 69 | + v-model="formData.video" | |
| 70 | + label="附件" | |
| 71 | + label-width="180rpx" | |
| 72 | + prop="video" | |
| 73 | + clearable | |
| 74 | + placeholder="请输入附件" | |
| 75 | + /> | |
| 76 | + <wd-input | |
| 77 | + v-model="formData.memo" | |
| 78 | + label="备注" | |
| 79 | + label-width="180rpx" | |
| 80 | + prop="memo" | |
| 81 | + clearable | |
| 82 | + placeholder="请输入备注" | |
| 83 | + /> | |
| 84 | + </wd-cell-group> | |
| 85 | + </wd-form> | |
| 86 | + </view> | |
| 87 | + | |
| 88 | + <!-- 底部保存按钮 --> | |
| 89 | + <view class="yd-detail-footer"> | |
| 90 | + <wd-button | |
| 91 | + type="primary" | |
| 92 | + block | |
| 93 | + :loading="formLoading" | |
| 94 | + @click="handleSubmit" | |
| 95 | + > | |
| 96 | + 保存 | |
| 97 | + </wd-button> | |
| 98 | + </view> | |
| 99 | + </view> | |
| 100 | +</template> | |
| 101 | + | |
| 102 | +<script lang="ts" setup> | |
| 103 | +import type { FormInstance } from 'wot-design-uni/components/wd-form/types' | |
| 104 | +import type { Student } from '@/api/infra/demo' | |
| 105 | +import { computed, onMounted, ref } from 'vue' | |
| 106 | +import { useToast } from 'wot-design-uni' | |
| 107 | +import { createStudent, getStudent, updateStudent } from '@/api/infra/demo' | |
| 108 | +import { getIntDictOptions, getBoolDictOptions } from '@/hooks/useDict' | |
| 109 | +import { navigateBackPlus } from '@/utils' | |
| 110 | +import { DICT_TYPE } from '@/utils/constants' | |
| 111 | + | |
| 112 | +const props = defineProps<{ | |
| 113 | + id?: number | any | |
| 114 | +}>() | |
| 115 | + | |
| 116 | +definePage({ | |
| 117 | + style: { | |
| 118 | + navigationBarTitleText: '', | |
| 119 | + navigationStyle: 'custom', | |
| 120 | + }, | |
| 121 | +}) | |
| 122 | + | |
| 123 | +const toast = useToast() | |
| 124 | +const getTitle = computed(() => props.id ? '编辑学生' : '新增学生') | |
| 125 | +const formLoading = ref(false) // 表单提交状态 | |
| 126 | +const formData = ref<Student>({ | |
| 127 | + id: undefined, | |
| 128 | + name: '', | |
| 129 | + description: '', | |
| 130 | + birthday: undefined, | |
| 131 | + sex: 0, | |
| 132 | + enabled: false, | |
| 133 | + avatar: '', | |
| 134 | + video: '', | |
| 135 | + memo: '', | |
| 136 | +}) // 表单数据 | |
| 137 | +const formRules = { | |
| 138 | + name: [{ required: true, message: '名字不能为空' }], | |
| 139 | + description: [{ required: true, message: '简介不能为空' }], | |
| 140 | + birthday: [{ required: true, message: '出生日期不能为空' }], | |
| 141 | + sex: [{ required: true, message: '性别不能为空' }], | |
| 142 | + enabled: [{ required: true, message: '是否有效不能为空' }], | |
| 143 | + avatar: [{ required: true, message: '头像不能为空' }], | |
| 144 | + video: [{ required: true, message: '附件不能为空' }], | |
| 145 | + memo: [{ required: true, message: '备注不能为空' }], | |
| 146 | +} // 表单校验规则 | |
| 147 | +const formRef = ref<FormInstance>() // 表单组件引用 | |
| 148 | + | |
| 149 | +/** 返回上一页 */ | |
| 150 | +function handleBack() { | |
| 151 | + navigateBackPlus('/pages-infra/demo/index') | |
| 152 | +} | |
| 153 | + | |
| 154 | +/** 加载学生详情 */ | |
| 155 | +async function getDetail() { | |
| 156 | + if (!props.id) { | |
| 157 | + return | |
| 158 | + } | |
| 159 | + formData.value = await getStudent(props.id) | |
| 160 | +} | |
| 161 | + | |
| 162 | +/** 提交表单 */ | |
| 163 | +async function handleSubmit() { | |
| 164 | + const { valid } = await formRef.value.validate() | |
| 165 | + if (!valid) { | |
| 166 | + return | |
| 167 | + } | |
| 168 | + | |
| 169 | + formLoading.value = true | |
| 170 | + try { | |
| 171 | + if (props.id) { | |
| 172 | + await updateStudent(formData.value) | |
| 173 | + toast.success('修改成功') | |
| 174 | + } else { | |
| 175 | + await createStudent(formData.value) | |
| 176 | + toast.success('新增成功') | |
| 177 | + } | |
| 178 | + uni.$emit('infra:demo:reload') | |
| 179 | + setTimeout(() => { | |
| 180 | + handleBack() | |
| 181 | + }, 500) | |
| 182 | + } finally { | |
| 183 | + formLoading.value = false | |
| 184 | + } | |
| 185 | +} | |
| 186 | + | |
| 187 | +/** 初始化 */ | |
| 188 | +onMounted(() => { | |
| 189 | + getDetail() | |
| 190 | +}) | |
| 191 | +</script> | |
| 192 | + | |
| 193 | +<style lang="scss" scoped> | |
| 194 | +</style> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/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_one/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 | + <view class="yd-search-form-item"> | |
| 31 | + <view class="yd-search-form-label"> | |
| 32 | + 性别 | |
| 33 | + </view> | |
| 34 | + <wd-radio-group v-model="formData.sex" shape="button"> | |
| 35 | + <wd-radio :value="-1"> | |
| 36 | + 全部 | |
| 37 | + </wd-radio> | |
| 38 | + <wd-radio | |
| 39 | + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" | |
| 40 | + :key="dict.value" | |
| 41 | + :value="dict.value" | |
| 42 | + > | |
| 43 | + {{ dict.label }} | |
| 44 | + </wd-radio> | |
| 45 | + </wd-radio-group> | |
| 46 | + </view> | |
| 47 | + <view class="yd-search-form-item"> | |
| 48 | + <view class="yd-search-form-label"> | |
| 49 | + 是否有效 | |
| 50 | + </view> | |
| 51 | + <wd-radio-group v-model="formData.enabled" shape="button"> | |
| 52 | + <wd-radio :value="-1"> | |
| 53 | + 全部 | |
| 54 | + </wd-radio> | |
| 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 | + </view> | |
| 64 | + <view class="yd-search-form-item"> | |
| 65 | + <view class="yd-search-form-label"> | |
| 66 | + 创建时间 | |
| 67 | + </view> | |
| 68 | + <view class="yd-search-form-date-range-container"> | |
| 69 | + <view @click="visibleCreateTime[0] = true"> | |
| 70 | + <view class="yd-search-form-date-range-picker"> | |
| 71 | + {{ formatDate(formData.createTime?.[0]) || '开始日期' }} | |
| 72 | + </view> | |
| 73 | + </view> | |
| 74 | + - | |
| 75 | + <view @click="visibleCreateTime[1] = true"> | |
| 76 | + <view class="yd-search-form-date-range-picker"> | |
| 77 | + {{ formatDate(formData.createTime?.[1]) || '结束日期' }} | |
| 78 | + </view> | |
| 79 | + </view> | |
| 80 | + </view> | |
| 81 | + <wd-datetime-picker-view v-if="visibleCreateTime[0]" v-model="tempCreateTime[0]" type="date" /> | |
| 82 | + <view v-if="visibleCreateTime[0]" class="yd-search-form-date-range-actions"> | |
| 83 | + <wd-button size="small" plain @click="visibleCreateTime[0] = false"> | |
| 84 | + 取消 | |
| 85 | + </wd-button> | |
| 86 | + <wd-button size="small" type="primary" @click="handleCreateTime0Confirm"> | |
| 87 | + 确定 | |
| 88 | + </wd-button> | |
| 89 | + </view> | |
| 90 | + <wd-datetime-picker-view v-if="visibleCreateTime[1]" v-model="tempCreateTime[1]" type="date" /> | |
| 91 | + <view v-if="visibleCreateTime[1]" class="yd-search-form-date-range-actions"> | |
| 92 | + <wd-button size="small" plain @click="visibleCreateTime[1] = false"> | |
| 93 | + 取消 | |
| 94 | + </wd-button> | |
| 95 | + <wd-button size="small" type="primary" @click="handleCreateTime1Confirm"> | |
| 96 | + 确定 | |
| 97 | + </wd-button> | |
| 98 | + </view> | |
| 99 | + </view> | |
| 100 | + <view class="yd-search-form-actions"> | |
| 101 | + <wd-button class="flex-1" plain @click="handleReset"> | |
| 102 | + 重置 | |
| 103 | + </wd-button> | |
| 104 | + <wd-button class="flex-1" type="primary" @click="handleSearch"> | |
| 105 | + 搜索 | |
| 106 | + </wd-button> | |
| 107 | + </view> | |
| 108 | + </view> | |
| 109 | + </wd-popup> | |
| 110 | +</template> | |
| 111 | + | |
| 112 | +<script lang="ts" setup> | |
| 113 | +import { computed, reactive, ref } from 'vue' | |
| 114 | +import { getNavbarHeight } from '@/utils' | |
| 115 | +import { formatDate, formatDateRange } from '@/utils/date' | |
| 116 | +import { getDictLabel, getIntDictOptions, getBoolDictOptions } from '@/hooks/useDict' | |
| 117 | +import { DICT_TYPE } from '@/utils/constants' | |
| 118 | + | |
| 119 | +const emit = defineEmits<{ | |
| 120 | + search: [data: Record<string, any>] | |
| 121 | + reset: [] | |
| 122 | +}>() | |
| 123 | + | |
| 124 | +const visible = ref(false) // 搜索弹窗显示状态 | |
| 125 | +const formData = reactive({ | |
| 126 | + name: undefined as string | undefined, | |
| 127 | + birthday: undefined as string | undefined, | |
| 128 | + sex: -1, // -1 表示全部 | |
| 129 | + enabled: -1 as -1 | boolean, // -1 表示全部 | |
| 130 | + createTime: [undefined, undefined] as [number | undefined, number | undefined], | |
| 131 | +}) // 搜索表单数据 | |
| 132 | + | |
| 133 | +/** 搜索条件 placeholder 拼接 */ | |
| 134 | +const placeholder = computed(() => { | |
| 135 | + const conditions: string[] = [] | |
| 136 | + if (formData.name) { | |
| 137 | + conditions.push(`名字:${formData.name}`) | |
| 138 | + } | |
| 139 | + if (formData.birthday) { | |
| 140 | + conditions.push(`出生日期:${formData.birthday}`) | |
| 141 | + } | |
| 142 | + if (formData.sex !== -1) { | |
| 143 | + conditions.push(`性别:${getDictLabel(DICT_TYPE.SYSTEM_USER_SEX, formData.sex)}`) | |
| 144 | + } | |
| 145 | + if (formData.enabled !== -1) { | |
| 146 | + conditions.push(`是否有效:${getDictLabel(DICT_TYPE.INFRA_BOOLEAN_STRING, formData.enabled)}`) | |
| 147 | + } | |
| 148 | + if (formData.createTime?.[0] && formData.createTime?.[1]) { | |
| 149 | + conditions.push(`创建时间:${formatDate(formData.createTime[0])}~${formatDate(formData.createTime[1])}`) | |
| 150 | + } | |
| 151 | + return conditions.length > 0 ? conditions.join(' | ') : '搜索学生' | |
| 152 | +}) | |
| 153 | + | |
| 154 | +const visibleCreateTime = ref<[boolean, boolean]>([false, false]) // 创建时间选择器状态 | |
| 155 | +const tempCreateTime = ref<[number, number]>([Date.now(), Date.now()]) // 创建时间临时值 | |
| 156 | + | |
| 157 | +/** 确认创建时间开始日期 */ | |
| 158 | +function handleCreateTime0Confirm() { | |
| 159 | + formData.createTime = [tempCreateTime.value[0], formData.createTime?.[1]] | |
| 160 | + visibleCreateTime.value[0] = false | |
| 161 | +} | |
| 162 | + | |
| 163 | +/** 确认创建时间结束日期 */ | |
| 164 | +function handleCreateTime1Confirm() { | |
| 165 | + formData.createTime = [formData.createTime?.[0], tempCreateTime.value[1]] | |
| 166 | + visibleCreateTime.value[1] = false | |
| 167 | +} | |
| 168 | + | |
| 169 | +/** 搜索按钮操作 */ | |
| 170 | +function handleSearch() { | |
| 171 | + visible.value = false | |
| 172 | + emit('search', { | |
| 173 | + ...formData, | |
| 174 | + sex: formData.sex === -1 ? undefined : formData.sex, | |
| 175 | + enabled: formData.enabled === -1 ? undefined : formData.enabled, | |
| 176 | + createTime: formatDateRange(formData.createTime), | |
| 177 | + }) | |
| 178 | +} | |
| 179 | + | |
| 180 | +/** 重置按钮操作 */ | |
| 181 | +function handleReset() { | |
| 182 | + formData.name = undefined | |
| 183 | + formData.birthday = undefined | |
| 184 | + formData.sex = -1 | |
| 185 | + formData.enabled = -1 | |
| 186 | + formData.createTime = [undefined, undefined] | |
| 187 | + visible.value = false | |
| 188 | + emit('reset') | |
| 189 | +} | |
| 190 | +</script> | ... | ... |
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/xml/InfraStudentMapper
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |
| 3 | +<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper"> | |
| 4 | + | |
| 5 | + <!-- | |
| 6 | + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。 | |
| 7 | + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。 | |
| 8 | + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。 | |
| 9 | + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/ | |
| 10 | + --> | |
| 11 | + | |
| 12 | +</mapper> | |
| 0 | 13 | \ No newline at end of file | ... | ... |