Commit f6b5ef06d9481d3d4141463c18ed26f278ef256b

Authored by YunaiV
1 parent adbec22e

feat(codegen):uniapp 表单生成器对齐 @wot-ui/ui 2.x + 修测试快照撞名

form 模板全量对齐 2.x:
- :rules/formRules → :schema/createFormSchema(2.x wd-form 只认 schema)
- 字段统一包 wd-form-item;下拉字典 → yd-form-picker 独立;radio shape→type
- 日期时间 → wd-form-item 触发 + wd-datetime-picker 弹层 + xxxVisible;
  LocalDateTime 用 formatDateTime、Date/LocalDate 用 formatDate(import 按需)
- 图片/文件上传 → yd-upload-img/file(原误生成文本框);小数补 precision
- 包名 wot-design-uni(未装)→ @wot-ui/ui(form + detail)

search-form:日期范围 → yd-search-date-range;radio shape→type
字典 import 按实际用量门控(纯 select 不越界)
writeResult:同名快照撞名补父目录 + 序号兜底,assert 路径不变
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/components/search-form.vue.vm
... ... @@ -40,49 +40,13 @@
40 40 />
41 41 </view>
42 42 #elseif ($column.htmlType == "datetime" && $listOperationCondition == "BETWEEN")
43   - #set ($AttrName = $javaField.substring(0,1).toUpperCase() + ${javaField.substring(1)})
44   - <view class="yd-search-form-item">
45   - <view class="yd-search-form-label">
46   - ${comment}
47   - </view>
48   - <view class="yd-search-form-date-range-container">
49   - <view @click="visible${AttrName}[0] = true">
50   - <view class="yd-search-form-date-range-picker">
51   - {{ formatDate(formData.${javaField}?.[0]) || '开始日期' }}
52   - </view>
53   - </view>
54   - -
55   - <view @click="visible${AttrName}[1] = true">
56   - <view class="yd-search-form-date-range-picker">
57   - {{ formatDate(formData.${javaField}?.[1]) || '结束日期' }}
58   - </view>
59   - </view>
60   - </view>
61   - <wd-datetime-picker-view v-if="visible${AttrName}[0]" v-model="temp${AttrName}[0]" type="date" />
62   - <view v-if="visible${AttrName}[0]" class="yd-search-form-date-range-actions">
63   - <wd-button size="small" plain @click="visible${AttrName}[0] = false">
64   - 取消
65   - </wd-button>
66   - <wd-button size="small" type="primary" @click="handle${AttrName}0Confirm">
67   - 确定
68   - </wd-button>
69   - </view>
70   - <wd-datetime-picker-view v-if="visible${AttrName}[1]" v-model="temp${AttrName}[1]" type="date" />
71   - <view v-if="visible${AttrName}[1]" class="yd-search-form-date-range-actions">
72   - <wd-button size="small" plain @click="visible${AttrName}[1] = false">
73   - 取消
74   - </wd-button>
75   - <wd-button size="small" type="primary" @click="handle${AttrName}1Confirm">
76   - 确定
77   - </wd-button>
78   - </view>
79   - </view>
  43 + <yd-search-date-range v-model="formData.${javaField}" label="${comment}" />
80 44 #elseif (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
81 45 <view class="yd-search-form-item">
82 46 <view class="yd-search-form-label">
83 47 ${comment}
84 48 </view>
85   - <wd-radio-group v-model="formData.${javaField}" shape="button">
  49 + <wd-radio-group v-model="formData.${javaField}" type="button">
86 50 <wd-radio :value="-1">
87 51 全部
88 52 </wd-radio>
... ... @@ -236,29 +200,6 @@ const placeholder = computed(() =&gt; {
236 200 return conditions.length > 0 ? conditions.join(' | ') : '搜索${table.classComment}'
237 201 })
238 202  
239   -#if ($hasDateTimeBetween == 1)
240   -#foreach($column in $columns)
241   - #if ($column.listOperation && $column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
242   - #set ($javaField = $column.javaField)
243   - #set ($AttrName = $javaField.substring(0,1).toUpperCase() + ${javaField.substring(1)})
244   -const visible${AttrName} = ref<[boolean, boolean]>([false, false]) // ${column.columnComment}选择器状态
245   -const temp${AttrName} = ref<[number, number]>([Date.now(), Date.now()]) // ${column.columnComment}临时值
246   -
247   -/** 确认${column.columnComment}开始日期 */
248   -function handle${AttrName}0Confirm() {
249   - formData.${javaField} = [temp${AttrName}.value[0], formData.${javaField}?.[1]]
250   - visible${AttrName}.value[0] = false
251   -}
252   -
253   -/** 确认${column.columnComment}结束日期 */
254   -function handle${AttrName}1Confirm() {
255   - formData.${javaField} = [formData.${javaField}?.[0], temp${AttrName}.value[1]]
256   - visible${AttrName}.value[1] = false
257   -}
258   - #end
259   -#end
260   -#end
261   -
262 203 /** 搜索按钮操作 */
263 204 function handleSearch() {
264 205 visible.value = false
... ...
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail/index.vue.vm
... ... @@ -64,7 +64,7 @@
64 64 import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
65 65 import { onUnload } from '@dcloudio/uni-app'
66 66 import { onMounted, ref } from 'vue'
67   -import { useToast } from 'wot-design-uni'
  67 +import { useToast } from '@wot-ui/ui/components/wd-toast'
68 68 import { delete${simpleClassName}, get${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
69 69 import { useAccess } from '@/hooks/useAccess'
70 70 import { navigateBackPlus } from '@/utils'
... ...
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/form/index.vue.vm
... ... @@ -9,7 +9,7 @@
9 9  
10 10 <!-- 表单区域 -->
11 11 <view>
12   - <wd-form ref="formRef" :model="formData" :rules="formRules">
  12 + <wd-form ref="formRef" :model="formData" :schema="formSchema">
13 13 <wd-cell-group border>
14 14 #foreach($column in $columns)
15 15 #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey)
... ... @@ -25,10 +25,23 @@
25 25 #elseif ($javaType == "Boolean")
26 26 #set ($dictMethod = "getBoolDictOptions")
27 27 #end
28   - ## 优先判断是否有字典,有字典则使用 radio-group
29   - #if (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
30   - <wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
31   - <wd-radio-group v-model="formData.${javaField}" shape="button">
  28 + ## 下拉选择(字典)→ yd-form-picker,内部按 dictType 解析选项,适合选项较多
  29 + #if ($column.htmlType == "select" && $dictType && "" != $dictType)
  30 + <yd-form-picker
  31 + v-model="formData.${javaField}"
  32 + label="${comment}"
  33 + label-width="180rpx"
  34 + prop="${javaField}"
  35 + :dict-type="DICT_TYPE.${dictType.toUpperCase()}"
  36 +#if ($javaType == "String")
  37 + dict-kind="str"
  38 +#end
  39 + placeholder="请选择${comment}"
  40 + />
  41 + ## 单选(字典)→ radio-group,适合选项较少
  42 + #elseif ($column.htmlType == "radio" && $dictType && "" != $dictType)
  43 + <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}" center>
  44 + <wd-radio-group v-model="formData.${javaField}" type="button">
32 45 <wd-radio
33 46 v-for="dict in $dictMethod(DICT_TYPE.${dictType.toUpperCase()})"
34 47 :key="dict.value"
... ... @@ -37,54 +50,78 @@
37 50 {{ dict.label }}
38 51 </wd-radio>
39 52 </wd-radio-group>
40   - </wd-cell>
  53 + </wd-form-item>
41 54 ## 数字类型(无字典)
42 55 #elseif (${javaType.toLowerCase()} == "long" || ${javaType.toLowerCase()} == "integer" || ${javaType.toLowerCase()} == "short" || ${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal" || ${javaType.toLowerCase()} == "byte")
43   - <wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
  56 + <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
44 57 <wd-input-number
45 58 v-model="formData.${javaField}"
  59 +#if (${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal")
  60 + :precision="2"
  61 + input-type="number"
  62 +#end
46 63 :min="0"
47 64 />
48   - </wd-cell>
  65 + </wd-form-item>
49 66 ## 布尔类型
50 67 #elseif (${javaType.toLowerCase()} == "boolean")
51   - <wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
  68 + <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}" center>
52 69 <wd-switch v-model="formData.${javaField}" />
53   - </wd-cell>
  70 + </wd-form-item>
54 71 ## 日期时间类型
55 72 #elseif (${javaType.toLowerCase()} == "date" || ${javaType.toLowerCase()} == "localdate" || ${javaType.toLowerCase()} == "localdatetime")
56 73 #set ($pickerType = "date")
57 74 #if (${javaType.toLowerCase()} == "localdatetime")
58 75 #set ($pickerType = "datetime")
59 76 #end
  77 + <wd-form-item
  78 + title="${comment}"
  79 + title-width="180rpx"
  80 + prop="${javaField}"
  81 + is-link
  82 +#if ($pickerType == "datetime")
  83 + :value="formatDateTime(formData.${javaField})"
  84 +#else
  85 + :value="formatDate(formData.${javaField})"
  86 +#end
  87 + placeholder="请选择${comment}"
  88 + @click="${javaField}Visible = true"
  89 + />
60 90 <wd-datetime-picker
61 91 v-model="formData.${javaField}"
  92 + v-model:visible="${javaField}Visible"
62 93 type="${pickerType}"
63   - label="${comment}"
64   - label-width="180rpx"
65   - prop="${javaField}"
66 94 />
67 95 ## 文本域
68 96 #elseif ($column.htmlType == "textarea")
69   - <wd-textarea
70   - v-model="formData.${javaField}"
71   - label="${comment}"
72   - label-width="180rpx"
73   - placeholder="请输入${comment}"
74   - :maxlength="200"
75   - show-word-limit
76   - clearable
77   - />
  97 + <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
  98 + <wd-textarea
  99 + v-model="formData.${javaField}"
  100 + clearable
  101 + placeholder="请输入${comment}"
  102 + :maxlength="200"
  103 + show-word-limit
  104 + />
  105 + </wd-form-item>
  106 + ## 图片上传 → yd-upload-img(包 wd-form-item 才能注册到 wd-form 做校验/错误展示)
  107 + #elseif ($column.htmlType == "imageUpload")
  108 + <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
  109 + <yd-upload-img v-model="formData.${javaField}" directory="${table.moduleName}/${table.businessName}" />
  110 + </wd-form-item>
  111 + ## 文件上传 → yd-upload-file
  112 + #elseif ($column.htmlType == "fileUpload")
  113 + <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
  114 + <yd-upload-file v-model="formData.${javaField}" directory="${table.moduleName}/${table.businessName}" />
  115 + </wd-form-item>
78 116 ## 默认:文本输入
79 117 #else
80   - <wd-input
81   - v-model="formData.${javaField}"
82   - label="${comment}"
83   - label-width="180rpx"
84   - prop="${javaField}"
85   - clearable
86   - placeholder="请输入${comment}"
87   - />
  118 + <wd-form-item title="${comment}" title-width="180rpx" prop="${javaField}">
  119 + <wd-input
  120 + v-model="formData.${javaField}"
  121 + clearable
  122 + placeholder="请输入${comment}"
  123 + />
  124 + </wd-form-item>
88 125 #end
89 126 #end
90 127 #end
... ... @@ -107,7 +144,7 @@
107 144 </template>
108 145  
109 146 <script lang="ts" setup>
110   -import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
  147 +import type { FormInstance } from '@wot-ui/ui/components/wd-form/types'
111 148 #set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
112 149 #if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
113 150 #set ($primaryTsType = "number")
... ... @@ -115,6 +152,8 @@ import type { FormInstance } from &#39;wot-design-uni/components/wd-form/types&#39;
115 152 #set ($primaryTsType = "string")
116 153 #end
117 154 #set ($hasDict = 0)
  155 +#set ($hasDate = 0)
  156 +#set ($hasDateTime = 0)
118 157 #set ($hasGetDictOptions = 0)
119 158 #set ($hasGetIntDictOptions = 0)
120 159 #set ($hasGetStrDictOptions = 0)
... ... @@ -124,22 +163,30 @@ import type { FormInstance } from &#39;wot-design-uni/components/wd-form/types&#39;
124 163 && ($column.htmlType == "select" || $column.htmlType == "radio")
125 164 && $column.dictType && "" != $column.dictType)
126 165 #set ($hasDict = 1)
127   - #if ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short")
  166 + #if ($column.htmlType == "radio" && ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short"))
128 167 #set ($hasGetIntDictOptions = 1)
129   - #elseif ($column.javaType == "String")
  168 + #elseif ($column.htmlType == "radio" && $column.javaType == "String")
130 169 #set ($hasGetStrDictOptions = 1)
131   - #elseif ($column.javaType == "Boolean")
  170 + #elseif ($column.htmlType == "radio" && $column.javaType == "Boolean")
132 171 #set ($hasGetBoolDictOptions = 1)
133   - #else
  172 + #elseif ($column.htmlType == "radio")
134 173 #set ($hasGetDictOptions = 1)
135 174 #end
136 175 #end
  176 + #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey
  177 + && (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate"))
  178 + #set ($hasDate = 1)
  179 + #end
  180 + #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey
  181 + && ${column.javaType.toLowerCase()} == "localdatetime")
  182 + #set ($hasDateTime = 1)
  183 + #end
137 184 #end
138 185 import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
139 186 import { computed, onMounted, ref } from 'vue'
140   -import { useToast } from 'wot-design-uni'
  187 +import { useToast } from '@wot-ui/ui/components/wd-toast'
141 188 import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
142   -#if ($hasDict == 1)
  189 +#if ($hasGetDictOptions == 1 || $hasGetIntDictOptions == 1 || $hasGetStrDictOptions == 1 || $hasGetBoolDictOptions == 1)
143 190 #set ($dictImportNames = "")
144 191 #if ($hasGetDictOptions == 1)
145 192 #set ($dictImportNames = "${dictImportNames}getDictOptions, ")
... ... @@ -161,6 +208,19 @@ import { navigateBackPlus } from &#39;@/utils&#39;
161 208 #if ($hasDict == 1)
162 209 import { DICT_TYPE } from '@/utils/constants'
163 210 #end
  211 +#if ($hasDate == 1 || $hasDateTime == 1)
  212 + #set ($dateImportNames = "")
  213 + #if ($hasDate == 1)
  214 + #set ($dateImportNames = "${dateImportNames}formatDate, ")
  215 + #end
  216 + #if ($hasDateTime == 1)
  217 + #set ($dateImportNames = "${dateImportNames}formatDateTime, ")
  218 + #end
  219 + #set ($dateImportNames = $dateImportNames.trim())
  220 + #set ($dateImportNames = $dateImportNames.substring(0, $dateImportNames.length() - 1))
  221 +import { $dateImportNames } from '@/utils/date'
  222 +#end
  223 +import { createFormSchema } from '@/utils/wot'
164 224  
165 225 const props = defineProps<{
166 226 id?: ${primaryTsType} | any
... ... @@ -196,7 +256,12 @@ const formData = ref&lt;${simpleClassName}&gt;({
196 256 #end
197 257 #end
198 258 }) // 表单数据
199   -const formRules = {
  259 +#foreach($column in $columns)
  260 + #if (($column.createOperation || $column.updateOperation) && !$column.primaryKey && (${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime"))
  261 +const ${column.javaField}Visible = ref(false) // ${column.columnComment}选择器状态
  262 + #end
  263 +#end
  264 +const formSchema = createFormSchema({
200 265 #foreach($column in $columns)
201 266 #set ($javaFieldLower = $column.javaField.toLowerCase())
202 267 #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey
... ... @@ -204,7 +269,7 @@ const formRules = {
204 269 ${column.javaField}: [{ required: true, message: '${column.columnComment}不能为空' }],
205 270 #end
206 271 #end
207   -} // 表单校验规则
  272 +}) // 表单校验规则
208 273 const formRef = ref<FormInstance>() // 表单组件引用
209 274  
210 275 /** 返回上一页 */
... ...
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java
... ... @@ -152,10 +152,21 @@ public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest {
152 152 protected void writeResult(Map<String, String> result, String basePath) {
153 153 // 写入文件内容
154 154 List<Map<String, String>> asserts = new ArrayList<>();
  155 + Set<String> usedContentPaths = new LinkedHashSet<>();
155 156 result.forEach((filePath, fileContent) -> {
156 157 String lastFilePath = StrUtil.subAfter(filePath, '/', true);
157   - String contentPath = StrUtil.subAfter(lastFilePath, '.', true)
158   - + '/' + StrUtil.subBefore(lastFilePath, '.', true);
  158 + String ext = StrUtil.subAfter(lastFilePath, '.', true);
  159 + String name = StrUtil.subBefore(lastFilePath, '.', true);
  160 + String contentPath = ext + '/' + name;
  161 + // 同名文件(如 index.vue 同时出现在 列表/form/detail)会撞名,撞名时前缀补上级目录区分;仍撞则加序号兜底,避免快照互相覆盖
  162 + if (usedContentPaths.contains(contentPath)) {
  163 + String parentDir = StrUtil.subAfter(StrUtil.subBefore(filePath, '/' + lastFilePath, true), '/', true);
  164 + contentPath = ext + '/' + parentDir + '/' + name;
  165 + for (int i = 2; usedContentPaths.contains(contentPath); i++) {
  166 + contentPath = ext + '/' + parentDir + '/' + name + '-' + i;
  167 + }
  168 + }
  169 + usedContentPaths.add(contentPath);
159 170 asserts.add(MapUtil.<String, String>builder().put("filePath", filePath)
160 171 .put("contentPath", contentPath).build());
161 172 FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath);
... ...
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/detail/index
... ... @@ -51,7 +51,7 @@
51 51 import type { Student } from '@/api/infra/demo'
52 52 import { onUnload } from '@dcloudio/uni-app'
53 53 import { onMounted, ref } from 'vue'
54   -import { useToast } from 'wot-design-uni'
  54 +import { useToast } from '@wot-ui/ui/components/wd-toast'
55 55 import { deleteStudent, getStudent } from '@/api/infra/demo'
56 56 import { useAccess } from '@/hooks/useAccess'
57 57 import { navigateBackPlus } from '@/utils'
... ...
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/form/index
... ... @@ -9,45 +9,48 @@
9 9  
10 10 <!-- 表单区域 -->
11 11 <view>
12   - <wd-form ref="formRef" :model="formData" :rules="formRules">
  12 + <wd-form ref="formRef" :model="formData" :schema="formSchema">
13 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
  14 + <wd-form-item title="名字" title-width="180rpx" prop="name">
  15 + <wd-input
  16 + v-model="formData.name"
  17 + clearable
  18 + placeholder="请输入名字"
  19 + />
  20 + </wd-form-item>
  21 + <wd-form-item title="简介" title-width="180rpx" prop="description">
  22 + <wd-textarea
  23 + v-model="formData.description"
  24 + clearable
  25 + placeholder="请输入简介"
  26 + :maxlength="200"
  27 + show-word-limit
  28 + />
  29 + </wd-form-item>
  30 + <wd-form-item
  31 + title="出生日期"
  32 + title-width="180rpx"
  33 + prop="birthday"
  34 + is-link
  35 + :value="formatDateTime(formData.birthday)"
  36 + placeholder="请选择出生日期"
  37 + @click="birthdayVisible = true"
30 38 />
31   - <wd-datetime-picker
  39 + <wd-datetime-picker
32 40 v-model="formData.birthday"
  41 + v-model:visible="birthdayVisible"
33 42 type="datetime"
34   - label="出生日期"
  43 + />
  44 + <yd-form-picker
  45 + v-model="formData.sex"
  46 + label="性别"
35 47 label-width="180rpx"
36   - prop="birthday"
  48 + prop="sex"
  49 + :dict-type="DICT_TYPE.SYSTEM_USER_SEX"
  50 + placeholder="请选择性别"
37 51 />
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">
  52 + <wd-form-item title="是否有效" title-width="180rpx" prop="enabled" center>
  53 + <wd-radio-group v-model="formData.enabled" type="button">
51 54 <wd-radio
52 55 v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
53 56 :key="dict.value"
... ... @@ -56,31 +59,20 @@
56 59 {{ dict.label }}
57 60 </wd-radio>
58 61 </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   - />
  62 + </wd-form-item>
  63 + <wd-form-item title="头像" title-width="180rpx" prop="avatar">
  64 + <yd-upload-img v-model="formData.avatar" directory="infra/demo" />
  65 + </wd-form-item>
  66 + <wd-form-item title="附件" title-width="180rpx" prop="video">
  67 + <yd-upload-file v-model="formData.video" directory="infra/demo" />
  68 + </wd-form-item>
  69 + <wd-form-item title="备注" title-width="180rpx" prop="memo">
  70 + <wd-input
  71 + v-model="formData.memo"
  72 + clearable
  73 + placeholder="请输入备注"
  74 + />
  75 + </wd-form-item>
84 76 </wd-cell-group>
85 77 </wd-form>
86 78 </view>
... ... @@ -100,14 +92,16 @@
100 92 </template>
101 93  
102 94 <script lang="ts" setup>
103   -import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
  95 +import type { FormInstance } from '@wot-ui/ui/components/wd-form/types'
104 96 import type { Student } from '@/api/infra/demo'
105 97 import { computed, onMounted, ref } from 'vue'
106   -import { useToast } from 'wot-design-uni'
  98 +import { useToast } from '@wot-ui/ui/components/wd-toast'
107 99 import { createStudent, getStudent, updateStudent } from '@/api/infra/demo'
108   -import { getIntDictOptions, getBoolDictOptions } from '@/hooks/useDict'
  100 +import { getBoolDictOptions } from '@/hooks/useDict'
109 101 import { navigateBackPlus } from '@/utils'
110 102 import { DICT_TYPE } from '@/utils/constants'
  103 +import { formatDateTime } from '@/utils/date'
  104 +import { createFormSchema } from '@/utils/wot'
111 105  
112 106 const props = defineProps<{
113 107 id?: number | any
... ... @@ -134,7 +128,8 @@ const formData = ref&lt;Student&gt;({
134 128 video: '',
135 129 memo: '',
136 130 }) // 表单数据
137   -const formRules = {
  131 +const birthdayVisible = ref(false) // 出生日期选择器状态
  132 +const formSchema = createFormSchema({
138 133 name: [{ required: true, message: '名字不能为空' }],
139 134 description: [{ required: true, message: '简介不能为空' }],
140 135 birthday: [{ required: true, message: '出生日期不能为空' }],
... ... @@ -143,7 +138,7 @@ const formRules = {
143 138 avatar: [{ required: true, message: '头像不能为空' }],
144 139 video: [{ required: true, message: '附件不能为空' }],
145 140 memo: [{ required: true, message: '备注不能为空' }],
146   -} // 表单校验规则
  141 +}) // 表单校验规则
147 142 const formRef = ref<FormInstance>() // 表单组件引用
148 143  
149 144 /** 返回上一页 */
... ...
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/search-form
... ... @@ -31,7 +31,7 @@
31 31 <view class="yd-search-form-label">
32 32 性别
33 33 </view>
34   - <wd-radio-group v-model="formData.sex" shape="button">
  34 + <wd-radio-group v-model="formData.sex" type="button">
35 35 <wd-radio :value="-1">
36 36 全部
37 37 </wd-radio>
... ... @@ -48,7 +48,7 @@
48 48 <view class="yd-search-form-label">
49 49 是否有效
50 50 </view>
51   - <wd-radio-group v-model="formData.enabled" shape="button">
  51 + <wd-radio-group v-model="formData.enabled" type="button">
52 52 <wd-radio :value="-1">
53 53 全部
54 54 </wd-radio>
... ... @@ -61,42 +61,7 @@
61 61 </wd-radio>
62 62 </wd-radio-group>
63 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>
  64 + <yd-search-date-range v-model="formData.createTime" label="创建时间" />
100 65 <view class="yd-search-form-actions">
101 66 <wd-button class="flex-1" plain @click="handleReset">
102 67 重置
... ... @@ -151,21 +116,6 @@ const placeholder = computed(() =&gt; {
151 116 return conditions.length > 0 ? conditions.join(' | ') : '搜索学生'
152 117 })
153 118  
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 119 /** 搜索按钮操作 */
170 120 function handleSearch() {
171 121 visible.value = false
... ...