Commit 33fb112dfecc23df103e8039f57097a451238519

Authored by YunaiV
1 parent 7041baf1

feat:【infra】移动端 admin uniapp 的代码生成的初始化

yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java
... ... @@ -23,6 +23,8 @@ public enum CodegenFrontTypeEnum {
23 23  
24 24 VUE3_VBEN5_EP_SCHEMA(50), // Vue3 VBEN5 + EP + schema 模版
25 25 VUE3_VBEN5_EP_GENERAL(51), // Vue3 VBEN5 + EP 标准模版
  26 +
  27 + VUE3_ADMIN_UNIAPP_WOT(60), // Vue3 Admin + Uniapp + WOT 标准模版
26 28 ;
27 29  
28 30 /**
... ...
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
... ... @@ -137,6 +137,16 @@ public class CodegenEngine {
137 137 vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue"))
138 138 .put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("api/api.ts"),
139 139 vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
  140 + .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("api/api.ts"),
  141 + vue3UniappFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
  142 + .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/index.vue"),
  143 + vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/index.vue"))
  144 + .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("components/search-form.vue"),
  145 + vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/components/search-form.vue"))
  146 + .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/form/index.vue"),
  147 + vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/form/index.vue"))
  148 + .put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/detail/index.vue"),
  149 + vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/detail/index.vue"))
140 150 // VUE3_VBEN2_ANTD_SCHEMA
141 151 .put(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType(), vue3VbenTemplatePath("views/data.ts"),
142 152 vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts"))
... ... @@ -617,6 +627,15 @@ public class CodegenEngine {
617 627 "src/" + path;
618 628 }
619 629  
  630 + private static String vue3AdminUniappTemplatePath(String path) {
  631 + return "codegen/vue3_admin_uniapp/" + path + ".vm";
  632 + }
  633 +
  634 + private static String vue3UniappFilePath(String path) {
  635 + return "yudao-ui-${sceneEnum.basePackage}-uniapp/" + // 顶级目录
  636 + "src/" + path;
  637 + }
  638 +
620 639 private static String vue3VbenFilePath(String path) {
621 640 return "yudao-ui-${sceneEnum.basePackage}-vben/" + // 顶级目录
622 641 "src/" + path;
... ...
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/api/api.ts.vm 0 → 100644
  1 +import type { PageParam, PageResult } from '@/http/types'
  2 +import { http } from '@/http/http'
  3 +
  4 +// TODO @AI:不使用 baseUrl,而是参考之前的,直接写在每个方法里。
  5 +#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
  6 +const baseUrl = '${baseURL}'
  7 +
  8 +#set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
  9 +#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
  10 +#set ($primaryTsType = "number")
  11 +#else
  12 +#set ($primaryTsType = "string")
  13 +#end
  14 +
  15 +/** ${table.classComment}信息 */
  16 +export interface ${simpleClassName} {
  17 +#foreach ($column in $columns)
  18 + #if ($column.primaryKey || $column.createOperation || $column.updateOperation || $column.listOperationResult)
  19 + #set ($javaType = $column.javaType.toLowerCase())
  20 + #set ($optional = $column.nullable || $column.primaryKey)
  21 + #if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte")
  22 + ${column.javaField}#if($optional)?#end: number
  23 + #elseif(${javaType} == "boolean")
  24 + ${column.javaField}#if($optional)?#end: boolean
  25 + #else
  26 + ${column.javaField}#if($optional)?#end: string
  27 + #end
  28 + #end
  29 +#end
  30 +}
  31 +
  32 +/** 获取${table.classComment}分页列表 */
  33 +export function get${simpleClassName}Page(params: PageParam) {
  34 + return http.get<PageResult<${simpleClassName}>>(baseUrl + '/page', params)
  35 +}
  36 +
  37 +/** 获取${table.classComment}详情 */
  38 +export function get${simpleClassName}(id: ${primaryTsType}) {
  39 + return http.get<${simpleClassName}>(baseUrl + '/get?id=' + id)
  40 +}
  41 +
  42 +/** 创建${table.classComment} */
  43 +export function create${simpleClassName}(data: ${simpleClassName}) {
  44 + return http.post<number>(baseUrl + '/create', data)
  45 +}
  46 +
  47 +/** 更新${table.classComment} */
  48 +export function update${simpleClassName}(data: ${simpleClassName}) {
  49 + return http.put<boolean>(baseUrl + '/update', data)
  50 +}
  51 +
  52 +/** 删除${table.classComment} */
  53 +export function delete${simpleClassName}(id: ${primaryTsType}) {
  54 + return http.delete<boolean>(baseUrl + '/delete?id=' + id)
  55 +}
... ...
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/components/search-form.vue.vm 0 → 100644
  1 +<template>
  2 + <wd-search
  3 + :placeholder="searchPlaceholder"
  4 + :hide-cancel="true"
  5 + disabled
  6 + @click="visible = true"
  7 + />
  8 +
  9 + <wd-popup
  10 + v-model="visible"
  11 + position="top"
  12 + custom-style="border-radius: 0 0 24rpx 24rpx;"
  13 + safe-area-inset-top
  14 + @close="visible = false"
  15 + >
  16 + <view class="p-32rpx">
  17 + <view class="mb-24rpx text-32rpx text-[#333] font-semibold">
  18 + 搜索${table.classComment}
  19 + </view>
  20 +
  21 +#set ($hasDict = 0)
  22 +#foreach ($column in $columns)
  23 + #if ($hasDict == 0 && $column.listOperation && $column.dictType && "" != $column.dictType)
  24 + #set ($hasDict = 1)
  25 + #end
  26 +#end
  27 +
  28 +#foreach($column in $columns)
  29 + #if ($column.listOperation)
  30 + #set ($dictType = $column.dictType)
  31 + #set ($javaField = $column.javaField)
  32 + #set ($javaType = $column.javaType)
  33 + #set ($comment = $column.columnComment)
  34 + #set ($dictMethod = "getDictOptions")
  35 + #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
  36 + #set ($dictMethod = "getIntDictOptions")
  37 + #elseif ($javaType == "String")
  38 + #set ($dictMethod = "getStrDictOptions")
  39 + #elseif ($javaType == "Boolean")
  40 + #set ($dictMethod = "getBoolDictOptions")
  41 + #end
  42 +
  43 + #if ($column.htmlType == "input")
  44 + <view class="mb-24rpx">
  45 + <view class="mb-12rpx text-28rpx text-[#666]">
  46 + ${comment}
  47 + </view>
  48 + <wd-input
  49 + v-model="formData.${javaField}"
  50 + placeholder="请输入${comment}"
  51 + clearable
  52 + />
  53 + </view>
  54 + #elseif (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
  55 + <view class="mb-32rpx">
  56 + <view class="mb-12rpx text-28rpx text-[#666]">
  57 + ${comment}
  58 + </view>
  59 + <wd-radio-group v-model="formData.${javaField}" shape="button" size="medium">
  60 + <wd-radio :value="-1">
  61 + 全部
  62 + </wd-radio>
  63 + <wd-radio
  64 + v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
  65 + :key="dict.value"
  66 + :value="dict.value"
  67 + >
  68 + {{ dict.label }}
  69 + </wd-radio>
  70 + </wd-radio-group>
  71 + </view>
  72 + #else
  73 + <view class="mb-24rpx">
  74 + <view class="mb-12rpx text-28rpx text-[#666]">
  75 + ${comment}
  76 + </view>
  77 + <wd-input
  78 + v-model="formData.${javaField}"
  79 + placeholder="请输入${comment}"
  80 + clearable
  81 + />
  82 + </view>
  83 + #end
  84 + #end
  85 +#end
  86 +
  87 + <view class="w-full flex justify-center gap-24rpx">
  88 + <wd-button class="flex-1" plain @click="handleReset">
  89 + 重置
  90 + </wd-button>
  91 + <wd-button class="flex-1" type="primary" @click="handleSearch">
  92 + 搜索
  93 + </wd-button>
  94 + </view>
  95 + </view>
  96 + </wd-popup>
  97 +</template>
  98 +
  99 +<script lang="ts" setup>
  100 +import { computed, reactive, ref, watch } from 'vue'
  101 +
  102 +#set ($hasDict = 0)
  103 +#set ($hasGetDictOptions = 0)
  104 +#set ($hasGetIntDictOptions = 0)
  105 +#set ($hasGetStrDictOptions = 0)
  106 +#set ($hasGetBoolDictOptions = 0)
  107 +#foreach($column in $columns)
  108 + #if ($column.listOperation && $column.dictType && "" != $column.dictType)
  109 + #set ($hasDict = 1)
  110 + #if ($column.htmlType == "select" || $column.htmlType == "radio")
  111 + #if ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short")
  112 + #set ($hasGetIntDictOptions = 1)
  113 + #elseif ($column.javaType == "String")
  114 + #set ($hasGetStrDictOptions = 1)
  115 + #elseif ($column.javaType == "Boolean")
  116 + #set ($hasGetBoolDictOptions = 1)
  117 + #else
  118 + #set ($hasGetDictOptions = 1)
  119 + #end
  120 + #end
  121 + #end
  122 +#end
  123 +#if ($hasDict == 1)
  124 +import { DICT_TYPE } from '@/utils/constants'
  125 +import {
  126 + #if ($hasGetDictOptions == 1)
  127 + getDictOptions,
  128 + #end
  129 + #if ($hasGetIntDictOptions == 1)
  130 + getIntDictOptions,
  131 + #end
  132 + #if ($hasGetStrDictOptions == 1)
  133 + getStrDictOptions,
  134 + #end
  135 + #if ($hasGetBoolDictOptions == 1)
  136 + getBoolDictOptions,
  137 + #end
  138 + getDictLabel,
  139 +} from '@/hooks/useDict'
  140 +#end
  141 +
  142 +/** 搜索表单数据 */
  143 +export interface SearchFormData {
  144 +#foreach($column in $columns)
  145 + #if ($column.listOperation)
  146 + #set ($javaType = $column.javaType.toLowerCase())
  147 + #if ($column.dictType && "" != $column.dictType)
  148 + #if(${javaType} == "string")
  149 + ${column.javaField}?: string | number
  150 + #elseif(${javaType} == "boolean")
  151 + ${column.javaField}?: boolean | number
  152 + #else
  153 + ${column.javaField}?: number
  154 + #end
  155 + #elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte")
  156 + ${column.javaField}?: number
  157 + #elseif(${javaType} == "boolean")
  158 + ${column.javaField}?: boolean
  159 + #else
  160 + ${column.javaField}?: string
  161 + #end
  162 + #end
  163 +#end
  164 +}
  165 +
  166 +const props = defineProps<{
  167 + searchParams?: Partial<SearchFormData>
  168 +}>()
  169 +
  170 +const emit = defineEmits<{
  171 + search: [data: SearchFormData]
  172 + reset: []
  173 +}>()
  174 +
  175 +const visible = ref(false)
  176 +
  177 +/** 搜索条件 placeholder 拼接 */
  178 +const searchPlaceholder = computed(() => {
  179 + const conditions: string[] = []
  180 +#foreach($column in $columns)
  181 + #if ($column.listOperation)
  182 + #set ($dictType = $column.dictType)
  183 + #set ($javaField = $column.javaField)
  184 + #set ($javaType = $column.javaType.toLowerCase())
  185 + #set ($comment = $column.columnComment)
  186 + #if ($dictType && "" != $dictType)
  187 + if (props.searchParams?.${javaField} !== undefined && props.searchParams.${javaField} !== -1) {
  188 + conditions.push('${comment}:' + getDictLabel(DICT_TYPE.$dictType.toUpperCase(), props.searchParams.${javaField}))
  189 + }
  190 + #else
  191 + #if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte" || ${javaType} == "boolean")
  192 + if (props.searchParams?.${javaField} !== undefined) {
  193 + conditions.push('${comment}:' + props.searchParams.${javaField})
  194 + }
  195 + #else
  196 + if (props.searchParams?.${javaField}) {
  197 + conditions.push('${comment}:' + props.searchParams.${javaField})
  198 + }
  199 + #end
  200 + #end
  201 + #end
  202 +#end
  203 + return conditions.length > 0 ? conditions.join(' | ') : '搜索${table.classComment}'
  204 +})
  205 +
  206 +const formData = reactive<SearchFormData>({
  207 +#foreach($column in $columns)
  208 + #if ($column.listOperation)
  209 + #if ($column.dictType && "" != $column.dictType)
  210 + #set ($javaType = $column.javaType.toLowerCase())
  211 + #if(${javaType} == "string")
  212 + ${column.javaField}: -1 as number | string,
  213 + #elseif(${javaType} == "boolean")
  214 + ${column.javaField}: -1 as number | boolean,
  215 + #else
  216 + ${column.javaField}: -1 as number,
  217 + #end
  218 + #else
  219 + ${column.javaField}: undefined,
  220 + #end
  221 + #end
  222 +#end
  223 +})
  224 +
  225 +watch(visible, (val) => {
  226 + if (val && props.searchParams) {
  227 +#foreach($column in $columns)
  228 + #if ($column.listOperation)
  229 + #if ($column.dictType && "" != $column.dictType)
  230 + formData.${column.javaField} = props.searchParams.${column.javaField} ?? -1
  231 + #else
  232 + formData.${column.javaField} = props.searchParams.${column.javaField}
  233 + #end
  234 + #end
  235 +#end
  236 + }
  237 +})
  238 +
  239 +function handleSearch() {
  240 + visible.value = false
  241 + emit('search', { ...formData } as SearchFormData)
  242 +}
  243 +
  244 +function handleReset() {
  245 +#foreach($column in $columns)
  246 + #if ($column.listOperation)
  247 + #if ($column.dictType && "" != $column.dictType)
  248 + formData.${column.javaField} = -1
  249 + #else
  250 + formData.${column.javaField} = undefined
  251 + #end
  252 + #end
  253 +#end
  254 + visible.value = false
  255 + emit('reset')
  256 +}
  257 +</script>
... ...
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail/index.vue.vm 0 → 100644
  1 +<template>
  2 + <view class="page-container">
  3 + <wd-navbar
  4 + title="${table.classComment}详情"
  5 + left-arrow placeholder safe-area-inset-top fixed
  6 + @click-left="handleBack"
  7 + />
  8 +
  9 + <view>
  10 + <wd-cell-group border>
  11 +#foreach($column in $columns)
  12 + #if ($column.primaryKey || $column.listOperationResult || $column.createOperation || $column.updateOperation)
  13 + #set ($javaField = $column.javaField)
  14 + #set ($comment = $column.columnComment)
  15 + #if ($column.dictType && "" != $column.dictType)
  16 + <wd-cell title="${comment}">
  17 + <dict-tag :type="DICT_TYPE.${column.dictType.toUpperCase()}" :value="formData?.${javaField}" />
  18 + </wd-cell>
  19 + #elseif ($column.javaType == "LocalDateTime")
  20 + <wd-cell title="${comment}" :value="formatDateTime(formData?.${javaField}) || '-'" />
  21 + #else
  22 + <wd-cell title="${comment}" :value="String(formData?.${javaField} ?? '-')" />
  23 + #end
  24 + #end
  25 +#end
  26 + </wd-cell-group>
  27 + </view>
  28 +
  29 + <view class="fixed bottom-0 left-0 right-0 bg-white p-24rpx">
  30 + <view class="w-full flex gap-24rpx">
  31 + <wd-button
  32 + v-if="hasAccessByCodes(['${permissionPrefix}:update'])"
  33 + class="flex-1" type="warning" @click="handleEdit"
  34 + >
  35 + 编辑
  36 + </wd-button>
  37 + <wd-button
  38 + v-if="hasAccessByCodes(['${permissionPrefix}:delete'])"
  39 + class="flex-1" type="error" :loading="deleting" @click="handleDelete"
  40 + >
  41 + 删除
  42 + </wd-button>
  43 + </view>
  44 + </view>
  45 + </view>
  46 +</template>
  47 +
  48 +<script lang="ts" setup>
  49 +import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
  50 +import { onMounted, ref } from 'vue'
  51 +import { useToast } from 'wot-design-uni'
  52 +import { delete${simpleClassName}, get${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
  53 +import { useAccess } from '@/hooks/useAccess'
  54 +import { navigateBackPlus } from '@/utils'
  55 +#set ($hasDict = 0)
  56 +#foreach($column in $columns)
  57 + #if ($hasDict == 0 && $column.dictType && "" != $column.dictType)
  58 + #set ($hasDict = 1)
  59 + #end
  60 +#end
  61 +#if ($hasDict == 1)
  62 +import { DICT_TYPE } from '@/utils/constants'
  63 +#end
  64 +#set ($hasDateTime = 0)
  65 +#foreach($column in $columns)
  66 + #if ($hasDateTime == 0 && $column.javaType == "LocalDateTime")
  67 + #set ($hasDateTime = 1)
  68 + #end
  69 +#end
  70 +#if ($hasDateTime == 1)
  71 +import { formatDateTime } from '@/utils/date'
  72 +#end
  73 +
  74 +const props = defineProps<{
  75 + id?: number | any
  76 +}>()
  77 +
  78 +definePage({
  79 + style: {
  80 + navigationBarTitleText: '',
  81 + navigationStyle: 'custom',
  82 + },
  83 +})
  84 +
  85 +const { hasAccessByCodes } = useAccess()
  86 +const toast = useToast()
  87 +const formData = ref<${simpleClassName}>()
  88 +const deleting = ref(false)
  89 +
  90 +function handleBack() {
  91 + navigateBackPlus('/pages-${table.moduleName}/${table.businessName}/index')
  92 +}
  93 +
  94 +async function getDetail() {
  95 + if (!props.id) {
  96 + return
  97 + }
  98 + try {
  99 + toast.loading('加载中...')
  100 + formData.value = await get${simpleClassName}(props.id)
  101 + } finally {
  102 + toast.close()
  103 + }
  104 +}
  105 +
  106 +function handleEdit() {
  107 + uni.navigateTo({
  108 + url: '/pages-${table.moduleName}/${table.businessName}/form/index?id=' + props.id,
  109 + })
  110 +}
  111 +
  112 +function handleDelete() {
  113 + if (!props.id) {
  114 + return
  115 + }
  116 + uni.showModal({
  117 + title: '提示',
  118 + content: '确定要删除该${table.classComment}吗?',
  119 + success: async (res) => {
  120 + if (!res.confirm) {
  121 + return
  122 + }
  123 + deleting.value = true
  124 + try {
  125 + await delete${simpleClassName}(props.id)
  126 + toast.success('删除成功')
  127 + setTimeout(() => {
  128 + handleBack()
  129 + }, 500)
  130 + } finally {
  131 + deleting.value = false
  132 + }
  133 + },
  134 + })
  135 +}
  136 +
  137 +onMounted(() => {
  138 + getDetail()
  139 +})
  140 +</script>
  141 +
  142 +<style lang="scss" scoped>
  143 +</style>
... ...
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/form/index.vue.vm 0 → 100644
  1 +<template>
  2 + <view class="page-container">
  3 + <wd-navbar
  4 + :title="getTitle"
  5 + left-arrow placeholder safe-area-inset-top fixed
  6 + @click-left="handleBack"
  7 + />
  8 +
  9 + <view>
  10 + <wd-form ref="formRef" :model="formData" :rules="formRules">
  11 + <wd-cell-group border>
  12 +#foreach($column in $columns)
  13 + #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey)
  14 + #set ($dictType = $column.dictType)
  15 + #set ($javaField = $column.javaField)
  16 + #set ($javaType = $column.javaType)
  17 + #set ($comment = $column.columnComment)
  18 + #set ($dictMethod = "getDictOptions")
  19 + #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
  20 + #set ($dictMethod = "getIntDictOptions")
  21 + #elseif ($javaType == "String")
  22 + #set ($dictMethod = "getStrDictOptions")
  23 + #elseif ($javaType == "Boolean")
  24 + #set ($dictMethod = "getBoolDictOptions")
  25 + #end
  26 +
  27 + #if (${javaType.toLowerCase()} == "long" || ${javaType.toLowerCase()} == "integer" || ${javaType.toLowerCase()} == "short" || ${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal" || ${javaType.toLowerCase()} == "byte")
  28 + <wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
  29 + <wd-input-number
  30 + v-model="formData.${javaField}"
  31 + :min="0"
  32 + />
  33 + </wd-cell>
  34 + #elseif (${javaType.toLowerCase()} == "boolean")
  35 + <wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
  36 + <wd-switch v-model="formData.${javaField}" />
  37 + </wd-cell>
  38 + #elseif ($column.htmlType == "textarea")
  39 + <wd-textarea
  40 + v-model="formData.${javaField}"
  41 + label="${comment}"
  42 + label-width="180rpx"
  43 + placeholder="请输入${comment}"
  44 + :maxlength="200"
  45 + show-word-limit
  46 + clearable
  47 + />
  48 + #elseif (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
  49 + <wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
  50 + <wd-radio-group v-model="formData.${javaField}" shape="button" size="medium">
  51 + <wd-radio
  52 + v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
  53 + :key="dict.value"
  54 + :value="dict.value"
  55 + >
  56 + {{ dict.label }}
  57 + </wd-radio>
  58 + </wd-radio-group>
  59 + </wd-cell>
  60 + #else
  61 + <wd-input
  62 + v-model="formData.${javaField}"
  63 + label="${comment}"
  64 + label-width="180rpx"
  65 + prop="${javaField}"
  66 + clearable
  67 + placeholder="请输入${comment}"
  68 + />
  69 + #end
  70 + #end
  71 +#end
  72 + </wd-cell-group>
  73 + </wd-form>
  74 + </view>
  75 +
  76 + <view class="fixed bottom-0 left-0 right-0 bg-white p-24rpx">
  77 + <wd-button
  78 + type="primary"
  79 + block
  80 + :loading="formLoading"
  81 + @click="handleSubmit"
  82 + >
  83 + 保存
  84 + </wd-button>
  85 + </view>
  86 + </view>
  87 +</template>
  88 +
  89 +<script lang="ts" setup>
  90 +import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
  91 +import { computed, onMounted, ref } from 'vue'
  92 +import { useToast } from 'wot-design-uni'
  93 +import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
  94 +import { navigateBackPlus } from '@/utils'
  95 +
  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 +
  103 +#set ($hasDict = 0)
  104 +#set ($hasGetDictOptions = 0)
  105 +#set ($hasGetIntDictOptions = 0)
  106 +#set ($hasGetStrDictOptions = 0)
  107 +#set ($hasGetBoolDictOptions = 0)
  108 +#foreach ($column in $columns)
  109 + #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey
  110 + && ($column.htmlType == "select" || $column.htmlType == "radio")
  111 + && $column.dictType && "" != $column.dictType)
  112 + #set ($hasDict = 1)
  113 + #if ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short")
  114 + #set ($hasGetIntDictOptions = 1)
  115 + #elseif ($column.javaType == "String")
  116 + #set ($hasGetStrDictOptions = 1)
  117 + #elseif ($column.javaType == "Boolean")
  118 + #set ($hasGetBoolDictOptions = 1)
  119 + #else
  120 + #set ($hasGetDictOptions = 1)
  121 + #end
  122 + #end
  123 +#end
  124 +#if ($hasDict == 1)
  125 +import { DICT_TYPE } from '@/utils/constants'
  126 +import {
  127 + #if ($hasGetDictOptions == 1)
  128 + getDictOptions,
  129 + #end
  130 + #if ($hasGetIntDictOptions == 1)
  131 + getIntDictOptions,
  132 + #end
  133 + #if ($hasGetStrDictOptions == 1)
  134 + getStrDictOptions,
  135 + #end
  136 + #if ($hasGetBoolDictOptions == 1)
  137 + getBoolDictOptions,
  138 + #end
  139 +} from '@/hooks/useDict'
  140 +#end
  141 +
  142 +const props = defineProps<{
  143 + id?: ${primaryTsType} | any
  144 +}>()
  145 +
  146 +definePage({
  147 + style: {
  148 + navigationBarTitleText: '',
  149 + navigationStyle: 'custom',
  150 + },
  151 +})
  152 +
  153 +const toast = useToast()
  154 +const getTitle = computed(() => props.id ? '编辑${table.classComment}' : '新增${table.classComment}')
  155 +const formLoading = ref(false)
  156 +
  157 +// TODO @AI:date + 时间范围,需要特殊处理;参考 /Users/yunai/Java/yudao-ui-admin-uniapp-next-v4/src/pages/message/components/search-form.vue 里的开始结束时间的处理;注意,index.vue.vm 也要调整;
  158 +const formData = ref<${simpleClassName}>({
  159 +#foreach($column in $columns)
  160 + #if (($column.createOperation || $column.updateOperation) || $column.primaryKey)
  161 + #set ($javaType = $column.javaType.toLowerCase())
  162 + #if ($column.primaryKey)
  163 + ${column.javaField}: undefined,
  164 + #elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte")
  165 + ${column.javaField}: 0,
  166 + #elseif(${javaType} == "boolean")
  167 + ${column.javaField}: false,
  168 + #else
  169 + ${column.javaField}: '',
  170 + #end
  171 + #end
  172 +#end
  173 +})
  174 +
  175 +const formRules = {
  176 +#foreach($column in $columns)
  177 + #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey)
  178 + ${column.javaField}: [{ required: true, message: '${column.columnComment}不能为空' }],
  179 + #end
  180 +#end
  181 +}
  182 +
  183 +const formRef = ref()
  184 +
  185 +function handleBack() {
  186 + navigateBackPlus('/pages-${table.moduleName}/${table.businessName}/index')
  187 +}
  188 +
  189 +async function getDetail() {
  190 + if (!props.id) {
  191 + return
  192 + }
  193 + formData.value = await get${simpleClassName}(props.id)
  194 +}
  195 +
  196 +async function handleSubmit() {
  197 + const { valid } = await formRef.value.validate()
  198 + if (!valid) {
  199 + return
  200 + }
  201 +
  202 + formLoading.value = true
  203 + try {
  204 + if (props.id) {
  205 + await update${simpleClassName}(formData.value)
  206 + toast.success('修改成功')
  207 + } else {
  208 + await create${simpleClassName}(formData.value)
  209 + toast.success('新增成功')
  210 + }
  211 + setTimeout(() => {
  212 + handleBack()
  213 + }, 500)
  214 + } finally {
  215 + formLoading.value = false
  216 + }
  217 +}
  218 +
  219 +onMounted(() => {
  220 + getDetail()
  221 +})
  222 +</script>
  223 +
  224 +<style lang="scss" scoped>
  225 +</style>
... ...
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/index.vue.vm 0 → 100644
  1 +<template>
  2 + <view class="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
  12 + :search-params="queryParams"
  13 + @search="handleQuery"
  14 + @reset="handleReset"
  15 + />
  16 +
  17 + <!-- ${table.classComment}列表 -->
  18 + <view class="p-24rpx">
  19 + <view
  20 + v-for="item in list"
  21 + :key="item.${primaryColumn.javaField}"
  22 + class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm"
  23 + @click="handleDetail(item)"
  24 + >
  25 + <!-- TODO @AI:下面的 view 部分代码,缩进可能不太对; -->
  26 + <view class="p-24rpx">
  27 +#set ($titleField = "")
  28 +#set ($statusField = "")
  29 +#set ($statusDictType = "")
  30 +#foreach($column in $columns)
  31 + #if ($titleField == "" && !$column.primaryKey && $column.listOperationResult)
  32 + #set ($titleField = $column.javaField)
  33 + #set ($titleComment = $column.columnComment)
  34 + #end
  35 + #if ($statusField == "" && $column.listOperationResult && $column.dictType && "" != $column.dictType)
  36 + #set ($statusField = $column.javaField)
  37 + #set ($statusDictType = $column.dictType)
  38 + #end
  39 +#end
  40 + <view class="mb-16rpx flex items-center justify-between">
  41 + <view class="text-32rpx text-[#333] font-semibold">
  42 + {{ item.#if($titleField != "")${titleField}#else ${primaryColumn.javaField}#end }}
  43 + </view>
  44 +#if($statusField != "")
  45 + <dict-tag :type="DICT_TYPE.$statusDictType.toUpperCase()" :value="item.$statusField" />
  46 +#end
  47 + </view>
  48 +#set ($infoCount = 0)
  49 +#foreach($column in $columns)
  50 + #if ($column.listOperationResult && !$column.primaryKey && $column.javaField != $titleField && $column.javaField != $statusField)
  51 + #if ($infoCount < 2)
  52 + <view class="mb-12rpx flex items-center text-28rpx text-[#666]">
  53 + <text class="mr-8rpx text-[#999]">${column.columnComment}:</text>
  54 + <text class="line-clamp-1">{{ item.${column.javaField} }}</text>
  55 + </view>
  56 + #set ($infoCount = $infoCount + 1)
  57 + #end
  58 + #end
  59 +#end
  60 + </view>
  61 + </view>
  62 +
  63 + <!-- 加载更多 -->
  64 + <view v-if="loadMoreState !== 'loading' && list.length === 0" class="py-100rpx text-center">
  65 + <wd-status-tip image="content" tip="暂无${table.classComment}数据" />
  66 + </view>
  67 + <wd-loadmore
  68 + v-if="list.length > 0"
  69 + :state="loadMoreState"
  70 + @reload="loadMore"
  71 + />
  72 + </view>
  73 +
  74 + <!-- 新增按钮 -->
  75 + <wd-fab
  76 + v-if="hasAccessByCodes(['${permissionPrefix}:create'])"
  77 + position="right-bottom"
  78 + type="primary"
  79 + :expandable="false"
  80 + @click="handleAdd"
  81 + />
  82 + </view>
  83 +</template>
  84 +
  85 +<script lang="ts" setup>
  86 +import type { SearchFormData } from './components/search-form.vue'
  87 +import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
  88 +import type { LoadMoreState } from '@/http/types'
  89 +import { onReachBottom } from '@dcloudio/uni-app'
  90 +import { onMounted, reactive, ref } from 'vue'
  91 +import { get${simpleClassName}Page } from '@/api/${table.moduleName}/${table.businessName}'
  92 +import { useAccess } from '@/hooks/useAccess'
  93 +import { navigateBackPlus } from '@/utils'
  94 +import SearchForm from './components/search-form.vue'
  95 +#set ($hasDict = 0)
  96 +#foreach($column in $columns)
  97 + #if ($hasDict == 0 && $column.listOperationResult && $column.dictType && "" != $column.dictType)
  98 + #set ($hasDict = 1)
  99 + #end
  100 +#end
  101 +#if ($hasDict == 1)
  102 +import { DICT_TYPE } from '@/utils/constants'
  103 +#end
  104 +
  105 +definePage({
  106 + style: {
  107 + navigationBarTitleText: '',
  108 + navigationStyle: 'custom',
  109 + },
  110 +})
  111 +
  112 +const { hasAccessByCodes } = useAccess()
  113 +const total = ref(0)
  114 +const list = ref<${simpleClassName}[]>([])
  115 +const loadMoreState = ref<LoadMoreState>('loading') // 加载更多状态
  116 +
  117 +const queryParams = reactive({
  118 + pageNo: 1,
  119 + pageSize: 10,
  120 +#foreach($column in $columns)
  121 + #if ($column.listOperation)
  122 + #set ($javaType = $column.javaType.toLowerCase())
  123 + #if ($column.dictType && "" != $column.dictType)
  124 + #if(${javaType} == "string")
  125 + ${column.javaField}: -1 as number | string, // -1 表示全部
  126 + #elseif(${javaType} == "boolean")
  127 + ${column.javaField}: -1 as number | boolean, // -1 表示全部
  128 + #else
  129 + ${column.javaField}: -1 as number, // -1 表示全部
  130 + #end
  131 + #elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte")
  132 + ${column.javaField}: undefined as number | undefined,
  133 + #elseif(${javaType} == "boolean")
  134 + ${column.javaField}: undefined as boolean | undefined,
  135 + #else
  136 + ${column.javaField}: undefined as string | undefined,
  137 + #end
  138 + #end
  139 +#end
  140 +})
  141 +
  142 +/** 返回上一页 */
  143 +function handleBack() {
  144 + navigateBackPlus()
  145 +}
  146 +
  147 +/** 查询${table.classComment}列表 */
  148 +async function getList() {
  149 + loadMoreState.value = 'loading'
  150 + try {
  151 + const data = await get${simpleClassName}Page({
  152 + ...queryParams,
  153 +#foreach($column in $columns)
  154 + #if ($column.listOperation && $column.dictType && "" != $column.dictType)
  155 + ${column.javaField}: queryParams.${column.javaField} === -1 ? undefined : queryParams.${column.javaField},
  156 + #end
  157 +#end
  158 + })
  159 + list.value = [...list.value, ...data.list]
  160 + total.value = data.total
  161 + loadMoreState.value = list.value.length >= total.value ? 'finished' : 'loading'
  162 + } catch {
  163 + queryParams.pageNo = queryParams.pageNo > 1 ? queryParams.pageNo - 1 : 1
  164 + loadMoreState.value = 'error'
  165 + }
  166 +}
  167 +
  168 +/** 搜索按钮操作 */
  169 +function handleQuery(data?: SearchFormData) {
  170 +#foreach($column in $columns)
  171 + #if ($column.listOperation)
  172 + queryParams.${column.javaField} = data?.${column.javaField}#if ($column.dictType && "" != $column.dictType) ?? -1#end
  173 + #end
  174 +#end
  175 + queryParams.pageNo = 1
  176 + list.value = []
  177 + getList()
  178 +}
  179 +
  180 +/** 重置按钮操作 */
  181 +function handleReset() {
  182 + handleQuery()
  183 +}
  184 +
  185 +/** 加载更多 */
  186 +function loadMore() {
  187 + if (loadMoreState.value === 'finished') {
  188 + return
  189 + }
  190 + queryParams.pageNo++
  191 + getList()
  192 +}
  193 +
  194 +/** 新增${table.classComment} */
  195 +function handleAdd() {
  196 + uni.navigateTo({
  197 + url: '/pages-${table.moduleName}/${table.businessName}/form/index',
  198 + })
  199 +}
  200 +
  201 +/** 查看详情 */
  202 +function handleDetail(item: ${simpleClassName}) {
  203 + uni.navigateTo({
  204 + url: '/pages-${table.moduleName}/${table.businessName}/detail/index?id=' + item.${primaryColumn.javaField},
  205 + })
  206 +}
  207 +
  208 +/** 触底加载更多 */
  209 +onReachBottom(() => {
  210 + loadMore()
  211 +})
  212 +
  213 +/** 初始化 */
  214 +onMounted(() => {
  215 + getList()
  216 +})
  217 +</script>
  218 +
  219 +<style lang="scss" scoped>
  220 +</style>
... ...