Commit 7e5256dcd7c88166813492507e9bf7001d893092

Authored by zhangdaiscott
1 parent 650f048b

代码生成器升级

1.支持插入菜单sql生成
2.vue3模板大升级
3.提供vue3原生表单模板生成
Showing 98 changed files with 5297 additions and 526 deletions
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/form/native/vue3NativeComponents.ftl 0 → 100644
  1 +<#if need_select_tag>
  2 + JDictSelectTag,
  3 +</#if>
  4 +<#if need_switch>
  5 + JSwitch,
  6 +</#if>
  7 +<#if need_multi>
  8 + JSelectMultiple,
  9 +</#if>
  10 +<#if need_search>
  11 + JSearchSelect,
  12 +</#if>
  13 +<#if need_popup>
  14 + JPopup,
  15 +</#if>
  16 +<#if need_category>
  17 + JCategorySelect,
  18 +</#if>
  19 +<#if need_dept>
  20 + JSelectDept,
  21 +</#if>
  22 +<#if need_dept_user>
  23 + JSelectUserByDept,
  24 +</#if>
  25 +<#if need_select_tree>
  26 + JTreeSelect,
  27 +</#if>
  28 +<#if need_time>
  29 + TimePicker,
  30 +</#if>
  31 +<#if need_pca>
  32 + JAreaLinkage,
  33 +</#if>
  34 +<#if need_upload>
  35 + JUpload,
  36 +</#if>
  37 +<#if need_image_upload>
  38 + JImageUpload,
  39 +</#if>
  40 +<#if need_markdown>
  41 + JMarkdownEditor,
  42 +</#if>
  43 +<#if need_editor>
  44 + JEditor,
  45 +</#if>
  46 +<#if need_checkbox>
  47 + JCheckbox,
  48 +</#if>
0 49 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/form/native/vue3NativeForm.ftl 0 → 100644
  1 +<#include "/common/utils.ftl">
  2 +<#if po.isShow =='Y' && po.fieldName != 'id' && isNotPidField(tableVo, po.fieldDbName)>
  3 +<#assign form_field_dictCode="">
  4 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  5 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  6 + <#elseif po.dictField?default("")?trim?length gt 1>
  7 + <#assign form_field_dictCode="${po.dictField}">
  8 + </#if>
  9 + <a-col :span="${form_span}">
  10 + <a-form-item label="${po.filedComment}" v-bind="validateInfos.${autoStringSuffixForModel(po)}">
  11 + <#if po.classType =='date'>
  12 + <a-date-picker placeholder="请选择${po.filedComment}" v-model:value="formData.${po.fieldName}" value-format="YYYY-MM-DD" style="width: 100%" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  13 + <#elseif po.classType =='datetime'>
  14 + <a-date-picker placeholder="请选择${po.filedComment}" v-model:value="formData.${po.fieldName}" showTime value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  15 + <#elseif po.classType =='time'>
  16 + <#assign need_time = true>
  17 + <time-picker placeholder="请选择${po.filedComment}" value-format="HH:mm:ss" v-model:value="formData.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  18 + <#elseif po.classType =='popup'>
  19 + <#assign need_popup = true>
  20 + <#assign sourceFields = po.dictField?default("")?trim?split(",")/>
  21 + <#assign targetFields = po.dictText?default("")?trim?split(",")/>
  22 + <j-popup
  23 + placeholder="请选择${po.filedComment}"
  24 + v-model:value="formData.${po.fieldName}"
  25 + code="${po.dictTable}"
  26 + :fieldConfig="[
  27 + <#list sourceFields as fieldName>
  28 + { source: '${fieldName}', target: '${targetFields[fieldName_index]}' },
  29 + </#list>
  30 + ]"
  31 + :multi="${po.extendParams.popupMulti?c}"
  32 + :setFieldsValue="setFieldsValue"
  33 + <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if><#rt>
  34 + />
  35 + <#elseif po.classType =='sel_depart'>
  36 + <#assign need_dept = true>
  37 + <j-select-dept v-model:value="formData.${po.fieldName}" :multiple="${po.extendParams.multi?default('true')}" checkStrictly <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> />
  38 + <#elseif po.classType =='switch'>
  39 + <#assign need_switch = true>
  40 + <j-switch v-model:value="formData.${po.fieldName}" <#if po.dictField != 'is_open'>:options="${po.dictField}"</#if> <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>></j-switch>
  41 + <#elseif po.classType =='pca'>
  42 + <#assign need_pca = true>
  43 + <j-area-linkage v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> />
  44 + <#elseif po.classType =='markdown'>
  45 + <#assign need_markdown = true>
  46 + <j-markdown-editor v-model:value="formData.${autoStringSuffixForModel(po)}" id="${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>></j-markdown-editor>
  47 + <#elseif po.classType =='password'>
  48 + <a-input-password v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  49 + <#elseif po.classType =='sel_user'>
  50 + <#assign need_dept_user = true>
  51 + <j-select-user-by-dept v-model:value="formData.${po.fieldName}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  52 + <#elseif po.classType =='textarea'>
  53 + <a-textarea v-model:value="formData.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  54 + <#elseif po.classType=='radio'>
  55 + <#assign need_select_tag = true>
  56 + <j-dict-select-tag type='radio' v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  57 + <#elseif po.classType=='list'>
  58 + <#assign need_select_tag = true>
  59 + <j-dict-select-tag v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  60 + <#elseif po.classType=='list_multi'>
  61 + <#assign need_multi = true>
  62 + <j-select-multiple type="${po.classType}" v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> :triggerChange="false"/>
  63 + <#elseif po.classType=='checkbox'>
  64 + <#assign need_checkbox = true>
  65 + <j-checkbox type="${po.classType}" v-model:value="formData.${po.fieldName}" dictCode="${form_field_dictCode}" placeholder="请选择${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  66 + <#elseif po.classType=='sel_search'>
  67 + <#assign need_search = true>
  68 + <j-search-select v-model:value="formData.${po.fieldName}" dict="${form_field_dictCode}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> />
  69 + <#elseif po.classType=='cat_tree'>
  70 + <#assign need_category = true>
  71 + <j-category-select v-model:value="formData.${po.fieldName}" pcode="${po.dictField?default("")}" placeholder="请选择${po.filedComment}" <#if po.dictText?default("")?trim?length gt 1>back="${dashedToCamel(po.dictText)}"</#if> <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> @change="(value) => handleFormChange('${po.fieldName}', value)" />
  72 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  73 + <a-input-number v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" style="width: 100%" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  74 + <#elseif po.classType=='file'>
  75 + <#assign need_upload = true>
  76 + <j-upload v-model:value="formData.${po.fieldName}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if> <#if po.uploadnum??>:maxCount=${po.uploadnum}</#if>></j-upload>
  77 + <#elseif po.classType=='image'>
  78 + <#assign need_image_upload = true>
  79 + <j-image-upload <#if po.uploadnum??>:fileMax=${po.uploadnum}</#if> v-model:value="formData.${po.fieldName}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>></j-image-upload>
  80 + <#elseif po.classType=='umeditor'>
  81 + <#assign need_editor = true>
  82 + <j-editor v-model:value="formData.${autoStringSuffixForModel(po)}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>/>
  83 + <#elseif po.fieldDbType=='Blob'>
  84 + <a-input v-model:value="formData.${autoStringSuffixForModel(po)}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>></a-input>
  85 + <#elseif po.classType == 'sel_tree'>
  86 + <#assign need_select_tree = true>
  87 + <j-tree-select
  88 + <#if po.dictText??>
  89 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  90 + dict="${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}"
  91 + <#elseif po.dictText?split(',')[1]??>
  92 + pidField="${po.dictText?split(',')[1]}"
  93 + <#elseif po.dictText?split(',')[3]??>
  94 + hasChildField="${po.dictText?split(',')[3]}"
  95 + </#if>
  96 + </#if>
  97 + pidValue="${po.dictField}"
  98 + <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>
  99 + v-model:value="formData.${po.fieldName}"
  100 + @change="(value) => handleFormChange('${po.fieldName}', value)">
  101 + </j-tree-select>
  102 + <#else>
  103 + <a-input v-model:value="formData.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>></a-input>
  104 + </#if>
  105 + </a-form-item>
  106 + </a-col>
  107 +</#if>
0 108 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/form/native/vue3NativeImport.ftl 0 → 100644
  1 +<#if need_select_tag>
  2 + import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
  3 +</#if>
  4 +<#if need_switch>
  5 + import JSwitch from '/@/components/Form/src/jeecg/components/JSwitch.vue';
  6 +</#if>
  7 +<#if need_multi>
  8 + import JSelectMultiple from '/@/components/Form/src/jeecg/components/JSelectMultiple.vue';
  9 +</#if>
  10 +<#if need_search>
  11 + import JSearchSelect from '/@/components/Form/src/jeecg/components/JSearchSelect.vue';
  12 +</#if>
  13 +<#if need_popup>
  14 + import JPopup from '/@/components/Form/src/jeecg/components/JPopup.vue';
  15 +</#if>
  16 +<#if need_category>
  17 + import JCategorySelect from '/@/components/Form/src/jeecg/components/JCategorySelect.vue';
  18 +</#if>
  19 +<#if need_dept>
  20 + import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
  21 +</#if>
  22 +<#if need_dept_user>
  23 + import JSelectUserByDept from '/@/components/Form/src/jeecg/components/JSelectUserByDept.vue';
  24 +</#if>
  25 +<#if need_select_tree>
  26 + import JTreeSelect from '/@/components/Form/src/jeecg/components/JTreeSelect.vue';
  27 +</#if>
  28 +<#if need_time>
  29 + import { TimePicker } from 'ant-design-vue';
  30 +</#if>
  31 +<#if need_pca>
  32 + import JAreaLinkage from '/@/components/Form/src/jeecg/components/JAreaLinkage.vue';
  33 +</#if>
  34 +<#if need_upload>
  35 + import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue';
  36 +</#if>
  37 +<#if need_image_upload>
  38 + import JImageUpload from '/@/components/Form/src/jeecg/components/JImageUpload.vue';
  39 +</#if>
  40 +<#if need_markdown>
  41 + import JMarkdownEditor from '/@/components/Form/src/jeecg/components/JMarkdownEditor.vue';
  42 +</#if>
  43 +<#if need_editor>
  44 + import JEditor from '/@/components/Form/src/jeecg/components/JEditor.vue';
  45 +</#if>
  46 +<#if need_checkbox>
  47 + import JCheckbox from "/@/components/Form/src/jeecg/components/JCheckbox.vue";
  48 +</#if>
0 49 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/form/native/vue3NativeSearch.ftl 0 → 100644
  1 +<#if po.isQuery=='Y'>
  2 +<#assign query_flag=true>
  3 + <#if query_field_no==2>
  4 + <template v-if="toggleSearchStatus">
  5 + </#if>
  6 + <#assign query_field_dictCode="">
  7 + <#if po.dictTable?default("")?trim?length gt 1>
  8 + <#assign need_select_tag = true>
  9 + <#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  10 + <#elseif po.dictField?default("")?trim?length gt 1>
  11 + <#assign need_select_tag = true>
  12 + <#assign query_field_dictCode="${po.dictField}">
  13 + </#if>
  14 + <#if po.queryMode=='single'>
  15 + <#if query_field_no gt 1> </#if><a-col :lg="8">
  16 + <#if query_field_no gt 1> </#if><a-form-item label="${po.filedComment}">
  17 + <#if po.classType=='sel_search'>
  18 + <#if query_field_no gt 1> </#if><j-search-select placeholder="请选择${po.filedComment}" v-model:value="queryParam.${po.fieldName}" dict="${po.dictTable},${po.dictText},${po.dictField}" />
  19 + <#elseif po.classType=='sel_user'>
  20 + <#if query_field_no gt 1> </#if><j-select-user-by-dept placeholder="请选择${po.filedComment}" v-model:value="queryParam.${po.fieldName}" />
  21 + <#elseif po.classType=='switch'>
  22 + <#if query_field_no gt 1> </#if><j-switch placeholder="请选择${po.filedComment}" v-model:value="queryParam.${po.fieldName}" <#if po.dictField!= 'is_open'>:options="${po.dictField}"</#if> query />
  23 + <#elseif po.classType=='sel_depart'>
  24 + <#if query_field_no gt 1> </#if><j-select-dept placeholder="请选择${po.filedComment}" v-model:value="queryParam.${po.fieldName}" checkStrictly />
  25 + <#elseif po.classType=='list_multi'>
  26 + <#if query_field_no gt 1> </#if><j-select-multiple placeholder="请选择${po.filedComment}" dictCode="${query_field_dictCode?default("")}" v-model:value="queryParam.${po.fieldName}" />
  27 + <#elseif po.classType=='cat_tree'>
  28 + <#if query_field_no gt 1> </#if><j-category-select placeholder="请选择${po.filedComment}" v-model:value="queryParam.${po.fieldName}" pcode="${po.dictField?default("")}" @change="(value) => handleFormChange('${po.fieldName}', value)" />
  29 + <#elseif po.classType=='date'>
  30 + <#if query_field_no gt 1> </#if><a-date-picker valueFormat="YYYY-MM-DD" placeholder="请选择${po.filedComment}" v-model:value="queryParam.${po.fieldName}" />
  31 + <#elseif po.classType=='datetime'>
  32 + <#if query_field_no gt 1> </#if><a-date-picker showTime valueFormat="YYYY-MM-DD HH:mm:ss" placeholder="请选择${po.filedComment}" v-model:value="queryParam.${po.fieldName}" />
  33 + <#elseif po.classType=='pca'>
  34 + <#if query_field_no gt 1> </#if><j-area-linkage v-model:value="queryParam.${po.fieldName}" placeholder="请选择${po.filedComment}" @change="(value) => handleAreaChange('${po.fieldName}', value)" />
  35 + <#elseif po.classType=='sel_tree'>
  36 + <#if query_field_no gt 1> </#if><j-tree-select v-model:value="queryParam.${po.fieldName}" placeholder="请选择${po.filedComment}" <#if po.dictText??><#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>dict="${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}" <#elseif po.dictText?split(',')[1]??>pidField:"${po.dictText?split(',')[1]}", <#elseif po.dictText?split(',')[3]??>hasChildField:"${po.dictText?split(',')[3]}"</#if> </#if>pidValue="${po.dictField}" />
  37 + <#elseif po.classType=='popup'>
  38 + <#assign sourceFields = po.dictField?default("")?trim?split(",")/>
  39 + <#assign targetFields = po.dictText?default("")?trim?split(",")/>
  40 + <#if query_field_no gt 1> </#if><j-popup
  41 + <#if query_field_no gt 1> </#if>placeholder="请选择${po.filedComment}"
  42 + <#if query_field_no gt 1> </#if>v-model:value="queryParam.${po.fieldName}"
  43 + <#if query_field_no gt 1> </#if>code="${po.dictTable}"
  44 + <#if query_field_no gt 1> </#if>:fieldConfig="[
  45 + <#list sourceFields as fieldName>
  46 + <#if query_field_no gt 1> </#if>{ source: '${fieldName}', target: '${targetFields[fieldName_index]}' },
  47 + </#list>
  48 + <#if query_field_no gt 1> </#if>]"
  49 + <#if query_field_no gt 1> </#if>:multi="${po.extendParams.popupMulti?c}"
  50 + <#if query_field_no gt 1> </#if>:setFieldsValue="setFieldsValue" />
  51 + <#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  52 + <#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
  53 + <#if po.dictTable?default("")?trim?length gt 1>
  54 + <#if query_field_no gt 1> </#if><j-dict-select-tag placeholder="请选择${po.filedComment}" v-model:value="queryParam.${po.fieldName}" dictCode="${po.dictTable},${po.dictText},${po.dictField}"/>
  55 + <#elseif po.dictField?default("")?trim?length gt 1>
  56 + <#if query_field_no gt 1> </#if><j-dict-select-tag placeholder="请选择${po.filedComment}" v-model:value="queryParam.${po.fieldName}" dictCode="${po.dictField}"/>
  57 + <#else>
  58 + <#if query_field_no gt 1> </#if><a-input placeholder="请输入${po.filedComment}" v-model:value="queryParam.${po.fieldName}"></a-input>
  59 + </#if>
  60 + <#else>
  61 + <#if query_field_no gt 1> </#if><a-input placeholder="请输入${po.filedComment}" v-model:value="queryParam.${po.fieldName}"></a-input>
  62 + </#if>
  63 + <#if query_field_no gt 1> </#if></a-form-item>
  64 + <#if query_field_no gt 1> </#if></a-col>
  65 + <#else>
  66 + <#if query_field_no gt 1> </#if><a-col :lg="8">
  67 + <#if query_field_no gt 1> </#if><a-form-item label="${po.filedComment}">
  68 + <#if po.classType=='date'>
  69 + <#if query_field_no gt 1> </#if><a-date-picker value-format="YYYY-MM-DD" placeholder="请选择开始时间" v-model:value="queryParam.${po.fieldName}_begin" class="query-group-cust"/>
  70 + <#if query_field_no gt 1> </#if><span class="query-group-split-cust">~</span>
  71 + <#if query_field_no gt 1> </#if><a-date-picker value-format="YYYY-MM-DD" placeholder="请选择结束日期" v-model:value="queryParam.${po.fieldName}_end" class="query-group-cust"/>
  72 + <#elseif po.classType=='datetime'>
  73 + <#if query_field_no gt 1> </#if><a-date-picker showTime value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择开始时间" v-model:value="queryParam.${po.fieldName}_begin" class="query-group-cust" />
  74 + <#if query_field_no gt 1> </#if><span class="query-group-split-cust">~</span>
  75 + <#if query_field_no gt 1> </#if><a-date-picker showTime value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择结束时间" v-model:value="queryParam.${po.fieldName}_end" class="query-group-cust" />
  76 + <#else>
  77 + <#if query_field_no gt 1> </#if><a-input placeholder="请输入最小值" v-model:value="queryParam.${po.fieldName}_begin" class="query-group-cust"></a-input>
  78 + <#if query_field_no gt 1> </#if><span class="query-group-split-cust">~</span>
  79 + <#if query_field_no gt 1> </#if><a-input placeholder="请输入最大值" v-model:value="queryParam.${po.fieldName}_end" class="query-group-cust"></a-input>
  80 + </#if>
  81 + <#if query_field_no gt 1> </#if></a-form-item>
  82 + <#if query_field_no gt 1> </#if></a-col>
  83 + </#if>
  84 + <#assign query_field_no=query_field_no+1>
  85 + </#if>
0 86 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/form/vue3Jvxepopup.ftl 0 → 100644
  1 +<#assign sourceFields = col.dictField?default("")?trim?split(",")/>
  2 +<#assign targetFields = col.dictText?default("")?trim?split(",")/>
  3 + type: JVxeTypes.popup,
  4 + popupCode:"${col.dictTable}",
  5 + fieldConfig: [
  6 + <#list sourceFields as fieldName>
  7 + { source: '${fieldName}', target: '${targetFields[fieldName_index]}' },
  8 + </#list>
  9 + ],
  10 + <#if col.readonly=='Y'>
  11 + disabled:true,
  12 + </#if>
  13 +
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/form/vue3popup.ftl 0 → 100644
  1 +<#assign sourceFields = po.dictField?default("")?trim?split(",")/>
  2 +<#assign targetFields = po.dictText?default("")?trim?split(",")/>
  3 + component: 'JPopup',
  4 + componentProps: ({ formActionType }) => {
  5 + const {setFieldsValue} = formActionType;
  6 + return{
  7 + setFieldsValue:setFieldsValue,
  8 + code:"${po.dictTable}",
  9 + fieldConfig: [
  10 + <#list sourceFields as fieldName>
  11 + { source: '${fieldName}', target: '${targetFields[fieldName_index]}' },
  12 + </#list>
  13 + ],
  14 + multi:${po.extendParams.popupMulti?c}
  15 + }
  16 + },
  17 +
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/sql/menu_insert.ftl 0 → 100644
  1 +-- 注意:该页面对应的前台目录为views/${entityPackage}文件夹下
  2 +-- 如果你想更改到其他目录,请修改sql中component字段对应的值
  3 +
  4 +INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external)
  5 +VALUES ('${.now?string["yyyyMMddhhmmSSsss"]}', NULL, '${tableVo.ftlDescription}', '/${entityPackage}/${entityName?uncap_first}List', '${entityPackage}/${entityName}List', NULL, NULL, 0, NULL, '1', 1.00, 0, NULL, 1, 1, 0, 0, 0, NULL, '1', 0, 0, 'admin', '${.now?string["yyyy-MM-dd HH:mm:ss"]}', NULL, NULL, 0);
0 6 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/utils.ftl
... ... @@ -25,7 +25,7 @@
25 25 <#return text?uncap_first>
26 26 </#function>
27 27 <#-- 驼峰转下划线 -->
28   -<#function camelToDashed(str, case='normal')>
  28 +<#function camelToDashed(str, case='lower')>
29 29 <#return camelToChar(str, "_", case)>
30 30 </#function>
31 31 <#---->
... ... @@ -130,4 +130,44 @@
130 130 <#return "{type:'${po.fieldDbType}',value:'${po.fieldName}',text:'${po.filedComment}'}">
131 131 </#if>
132 132 </#if>
  133 +</#function>
  134 +
  135 +
  136 +<#-- vue3 获取表单modal的宽度-->
  137 +<#function getModalWidth fieldRowNum>
  138 + <#assign modal_width = 800>
  139 + <#if fieldRowNum==2>
  140 + <#assign modal_width = 896>
  141 + <#elseif fieldRowNum==3>
  142 + <#assign modal_width = 1024>
  143 + <#elseif fieldRowNum==4>
  144 + <#assign modal_width = 1280>
  145 + </#if>
  146 + <#return modal_width>
  147 +</#function>
  148 +
  149 +<#-- vue3 获取表单 colspan -->
  150 +<#function getFormSpan fieldRowNum>
  151 + <#assign form_span = 24>
  152 + <#if fieldRowNum==2>
  153 + <#assign form_span = 12>
  154 + <#elseif fieldRowNum==3>
  155 + <#assign form_span = 8>
  156 + <#elseif fieldRowNum==4>
  157 + <#assign form_span = 6>
  158 + </#if>
  159 + <#return form_span>
  160 +</#function>
  161 +
  162 +<#-- vue3 native 判断字段名不是 pidField -->
  163 +<#function isNotPidField(tableVo, fieldDbName) >
  164 + <#assign flag = true>
  165 + <#if tableVo??>
  166 + <#if tableVo.extendParams??>
  167 + <#if tableVo.extendParams.pidField?default("")?trim == fieldDbName>
  168 + <#assign flag = false>
  169 + </#if>
  170 + </#if>
  171 + </#if>
  172 + <#return flag>
133 173 </#function>
134 174 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/validatorRulesTemplate/native/vue3CoreNative.ftl 0 → 100644
  1 +<#assign fieldValidType = po.fieldValidType!''>
  2 +<#-- 非空校验 -->
  3 +<#if po.nullable == 'N' || fieldValidType == '*'>
  4 +{ required: true, message: '请输入${po.filedComment}!'}<#rt>,
  5 +<#elseif fieldValidType!=''>
  6 +{ required: false}<#rt>,
  7 +</#if>
  8 +<#-- 唯一校验 -->
  9 +<#if fieldValidType == 'only'>
  10 + { validator: ${po.fieldName}Duplicatevalidate }<#rt>
  11 +<#-- 6到16位数字 -->
  12 +<#elseif fieldValidType == 'n6-16'>
  13 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'}<#rt>,
  14 +<#-- 6到16位任意字符 -->
  15 +<#elseif fieldValidType == '*6-16'>
  16 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'}<#rt>,
  17 +<#-- 6到18位字符串 -->
  18 +<#elseif fieldValidType == 's6-18'>
  19 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'}<#rt>,
  20 +<#-- 网址 -->
  21 +<#elseif fieldValidType == 'url'>
  22 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'}<#rt>,
  23 +<#-- 电子邮件 -->
  24 +<#elseif fieldValidType == 'e'>
  25 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'}<#rt>,
  26 +<#-- 手机号码 -->
  27 +<#elseif fieldValidType == 'm'>
  28 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'}<#rt>,
  29 +<#-- 邮政编码 -->
  30 +<#elseif fieldValidType == 'p'>
  31 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'}<#rt>,
  32 +<#-- 字母 -->
  33 +<#elseif fieldValidType == 's'>
  34 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'}<#rt>,
  35 +<#-- 数字 -->
  36 +<#elseif fieldValidType == 'n'>
  37 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'}<#rt>,
  38 +<#-- 整数 -->
  39 +<#elseif fieldValidType == 'z'>
  40 + { pattern: /^-?\d+$/, message: '请输入整数!'}<#rt>,
  41 +<#-- 金额 -->
  42 +<#elseif fieldValidType == 'money'>
  43 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'}<#rt>,
  44 +<#-- 正则校验 -->
  45 +<#elseif fieldValidType != '' && fieldValidType != '*'>
  46 + { pattern: '${fieldValidType}', message: '不符合校验规则!'}<#rt>,
  47 +<#else>
  48 + <#t>
  49 +</#if>
0 50 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/validatorRulesTemplate/native/vue3MainNative.ftl 0 → 100644
  1 +<#include "../../utils.ftl">
  2 +<#list columns as po>
  3 + <#if po.isShow == 'Y' && poHasCheck(po)>
  4 + ${po.fieldName}: [<#include "vue3CoreNative.ftl">],
  5 + </#if>
  6 +</#list>
0 7 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/validatorRulesTemplate/sub-vue3.ftl 0 → 100644
  1 +<#include "../utils.ftl">
  2 + <#if col.isShow == 'Y' && poHasCheck(col)>
  3 + validateRules: [
  4 + <#if col.fieldName != 'id'>
  5 + <#assign subFieldValidType = col.fieldValidType!''>
  6 + <#-- 非空校验 -->
  7 + <#if col.nullable == 'N' || fieldValidType == '*'>
  8 + { required: true, message: '${'$'}{title}不能为空' },
  9 + <#elseif fieldValidType!=''>
  10 + { required: false},
  11 + </#if>
  12 + <#-- 其他情况下,只要有值就被认为是正则校验 -->
  13 + <#if subFieldValidType?length gt 0>
  14 + <#assign subMessage = '格式不正确'>
  15 + <#if subFieldValidType == 'only' >
  16 + <#assign subMessage = '不能重复'>
  17 + </#if>
  18 + { pattern: "${subFieldValidType}", message: "${'$'}{title}${subMessage}" }
  19 + <#t>
  20 + </#if>
  21 + </#if>
  22 + ],
  23 + </#if>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
  1 +<#include "/common/utils.ftl">
1 2 package ${bussiPackage}.${entityPackage}.entity;
2 3  
3 4 import java.io.Serializable;
... ... @@ -36,9 +37,9 @@ public class ${entityName} implements Serializable {
36 37 <#-- 生成字典Code -->
37 38 <#assign list_field_dictCode="">
38 39 <#if po.classType='sel_user'>
39   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  40 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
40 41 <#elseif po.classType='sel_depart'>
41   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  42 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
42 43 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
43 44 <#if po.dictTable?default("")?trim?length gt 1>
44 45 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Form.vuei
... ... @@ -48,7 +48,7 @@
48 48 @input="popupCallback"
49 49 <#if po.readonly=='Y'>disabled</#if>/>
50 50 <#elseif po.classType =='sel_depart'>
51   - <j-select-depart v-model="model.${po.fieldName}" multi <#if po.readonly=='Y'>disabled</#if> />
  51 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
52 52 <#elseif po.classType =='switch'>
53 53 <j-switch v-model="model.${po.fieldName}" <#if po.dictField != 'is_open'>:options="${po.dictField}"</#if> <#if po.readonly=='Y'>disabled</#if>></j-switch>
54 54 <#elseif po.classType =='pca'>
... ... @@ -58,7 +58,7 @@
58 58 <#elseif po.classType =='password'>
59 59 <a-input-password v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
60 60 <#elseif po.classType =='sel_user'>
61   - <j-select-user-by-dep v-model="model.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if>/>
  61 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
62 62 <#elseif po.classType =='textarea'>
63 63 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
64 64 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei
... ... @@ -46,6 +46,10 @@
46 46 <template #htmlSlot="{text}">
47 47 <div v-html="text"></div>
48 48 </template>
  49 + <!--省市区字段回显插槽-->
  50 + <template #pcaSlot="{text}">
  51 + {{ getAreaTextByCode(text) }}
  52 + </template>
49 53 <template #fileSlot="{text}">
50 54 <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
51 55 <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
... ... @@ -64,6 +68,10 @@
64 68 import ${entityName}Modal from './components/${entityName}Modal.vue'
65 69 import {columns, searchFormSchema} from './${entityName}.data';
66 70 import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName}.api';
  71 + import { downloadFile } from '/@/utils/common/renderUtils';
  72 + <#if list_need_pca>
  73 + import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
  74 + </#if>
67 75 <#if list_need_category>
68 76 import { loadCategoryData } from '/@/api/common/api'
69 77 import { getAuthCache, setAuthCache } from '/@/utils/auth';
... ... @@ -84,6 +92,17 @@
84 92 schemas: searchFormSchema,
85 93 autoSubmitOnEnter:true,
86 94 showAdvancedButton:true,
  95 + fieldMapToNumber: [
  96 + <#list columns as po>
  97 + <#if po.isQuery=='Y'>
  98 + <#if po.queryMode!='single'>
  99 + <#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  100 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end']],
  101 + </#if>
  102 + </#if>
  103 + </#if>
  104 + </#list>
  105 + ],
87 106 fieldMapToTime: [
88 107 <#list columns as po>
89 108 <#if po.isQuery=='Y'>
... ... @@ -100,6 +119,7 @@
100 119 },
101 120 actionColumn: {
102 121 width: 120,
  122 + fixed:'right'
103 123 },
104 124 },
105 125 exportConfig: {
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi
  1 +<#include "/common/utils.ftl">
1 2 import {BasicColumn} from '/@/components/Table';
2 3 import {FormSchema} from '/@/components/Table';
3 4 import { rules} from '/@/utils/helper/validator';
... ... @@ -24,7 +25,7 @@ export const columns: BasicColumn[] = [
24 25 slots: { customRender: 'htmlSlot' },
25 26 <#elseif po.classType=='pca'>
26 27 dataIndex: '${po.fieldName}',
27   - slots: { customRender: 'pcaSlot' },//TODO 未翻译
  28 + slots: { customRender: 'pcaSlot' },
28 29 <#elseif po.classType=='file'>
29 30 dataIndex: '${po.fieldName}',
30 31 slots: { customRender: 'fileSlot' },
... ... @@ -56,7 +57,7 @@ export const columns: BasicColumn[] = [
56 57 return render.renderCategoryTree(text,'${po.dictField?default("")}')
57 58 },
58 59 <#else>
59   - customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  60 + customRender: ({text, record}) => (text ? record['${po.dictText}'] : '')
60 61 </#if>
61 62 <#else>
62 63 dataIndex: '${po.fieldName}'
... ... @@ -83,7 +84,7 @@ export const searchFormSchema: FormSchema[] = [
83 84 <#if po.queryMode=='single'>
84 85 {
85 86 label: "${po.filedComment}",
86   - field: "${po.fieldName}",
  87 + field: ${autoStringSuffix(po)},
87 88 <#if po.classType=='sel_search'>
88 89 component: 'JSearchSelect',
89 90 componentProps:{
... ... @@ -94,17 +95,23 @@ export const searchFormSchema: FormSchema[] = [
94 95 <#elseif po.classType=='switch'>
95 96 component: 'JSwitch',
96 97 componentProps:{
  98 + query:true,
97 99 <#if po.dictField != 'is_open'>
98   - options:"${po.dictField}"
  100 + options:${po.dictField}
99 101 </#if>
100 102 },
101 103 <#elseif po.classType=='sel_depart'>
102 104 component: 'JSelectDept',
103 105 <#elseif po.classType=='list_multi'>
104   - component: 'JMultiSelectTag',//暂无该组件
  106 + component: 'JSelectMultiple',
105 107 componentProps:{
106   - dictCode:"query_field_dictCode?default("")"
107   - },
  108 + <#if po.dictTable?default("")?trim?length gt 1>
  109 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}",
  110 + <#elseif po.dictField?default("")?trim?length gt 1>
  111 + dictCode:"${po.dictField}",
  112 + </#if>
  113 + triggerChange: true
  114 + },
108 115 <#elseif po.classType=='cat_tree'>
109 116 component: 'JCategorySelect',
110 117 componentProps:{
... ... @@ -117,19 +124,15 @@ export const searchFormSchema: FormSchema[] = [
117 124 componentProps: {
118 125 showTime:true
119 126 },
  127 + <#elseif po.classType =='time'>
  128 + component: 'TimePicker',
  129 + componentProps: {
  130 + valueFormat: 'HH:mm:ss'
  131 + },
120 132 <#elseif po.classType=='pca'>
121 133 component: 'JAreaLinkage',
122 134 <#elseif po.classType=='popup'>
123   - component: 'JPopup',
124   - componentProps: ({ formActionType }) => {
125   - const {setFieldsValue} = formActionType;
126   - return{
127   - setFieldsValue:setFieldsValue,
128   - code:"${po.dictTable}",
129   - fieldConfig:"${po.dictField}",
130   - multi:${po.extendParams.popupMulti?c},
131   - }
132   - },
  135 + <#include "/common/form/vue3popup.ftl">
133 136 <#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
134 137 <#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
135 138 component: 'JDictSelectTag',
... ... @@ -156,6 +159,8 @@ export const searchFormSchema: FormSchema[] = [
156 159 componentProps: {
157 160 showTime:true
158 161 },
  162 +<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  163 + component: 'JRangeNumber',
159 164 <#else>
160 165 component: 'Input', //TODO 范围查询
161 166 </#if>
... ... @@ -188,27 +193,22 @@ export const formSchema: FormSchema[] = [
188 193 </#if>
189 194 {
190 195 label: '${po.filedComment}',
191   - field: '${po.fieldName}',
  196 + field: ${autoStringSuffix(po)},
192 197 <#if po.classType =='date'>
193 198 component: 'DatePicker',
194   - <#elseif po.fieldType =='datetime'>
  199 + <#elseif po.classType =='datetime'>
195 200 component: 'DatePicker',
196 201 componentProps: {
197   - showTime:true
  202 + showTime: true,
  203 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
198 204 },
199   - <#elseif po.fieldType =='time'>
  205 + <#elseif po.classType =='time'>
200 206 component: 'TimePicker',
  207 + componentProps: {
  208 + valueFormat: 'HH:mm:ss'
  209 + },
201 210 <#elseif po.classType =='popup'>
202   - component: 'JPopup',
203   - componentProps: ({ formActionType }) => {
204   - const {setFieldsValue} = formActionType;
205   - return{
206   - setFieldsValue:setFieldsValue,
207   - code:"${po.dictTable}",
208   - fieldConfig:${po.dictField},
209   - multi:${po.extendParams.popupMulti?c},
210   - }
211   - },
  211 + <#include "/common/form/vue3popup.ftl">
212 212 <#elseif po.classType =='sel_depart'>
213 213 component: 'JSelectDept',
214 214 <#elseif po.classType =='switch'>
... ... @@ -230,14 +230,14 @@ export const formSchema: FormSchema[] = [
230 230 labelKey:'realname',
231 231 },
232 232 <#elseif po.classType =='textarea'>
233   - component: 'InputTextArea',//TODO 注意string转换问题
  233 + component: 'InputTextArea',
234 234 <#elseif po.classType=='list' || po.classType=='radio'>
235 235 component: 'JDictSelectTag',
236 236 componentProps:{
237 237 dictCode:"${form_field_dictCode}"
238 238 },
239 239 <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
240   - component: 'JMultiSelectTag',//TODO 暂无该组件
  240 + component: 'JSelectMultiple',
241 241 componentProps:{
242 242 dictCode:"${form_field_dictCode}"
243 243 },
... ... @@ -272,7 +272,7 @@ export const formSchema: FormSchema[] = [
272 272 </#if>
273 273 },
274 274 <#elseif po.classType=='umeditor'>
275   - component: 'JCodeEditor', //TODO String后缀暂未添加
  275 + component: 'JEditor',
276 276 <#elseif po.classType == 'sel_tree'>
277 277 component: 'JTreeSelect',
278 278 componentProps:{
... ... @@ -290,7 +290,6 @@ export const formSchema: FormSchema[] = [
290 290 <#else>
291 291 component: 'Input',
292 292 </#if>
293   - <#include "/common/utils.ftl">
294 293 <#if po.isShow == 'Y' && poHasCheck(po)>
295 294 dynamicRules: ({model,schema}) => {
296 295 <#if po.fieldName != 'id'>
... ... @@ -304,16 +303,16 @@ export const formSchema: FormSchema[] = [
304 303 </#if>
305 304 <#-- 唯一校验 -->
306 305 <#if fieldValidType == 'only'>
307   - {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  306 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
308 307 <#-- 6到16位数字 -->
309 308 <#elseif fieldValidType == 'n6-16'>
310 309 { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
311 310 <#-- 6到16位任意字符 -->
312 311 <#elseif fieldValidType == '*6-16'>
313 312 { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
314   - <#-- 6到18位字符串 -->
  313 + <#-- 6到18位字 -->
315 314 <#elseif fieldValidType == 's6-18'>
316   - { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  315 + { pattern: /^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
317 316 <#-- 网址 -->
318 317 <#elseif fieldValidType == 'url'>
319 318 { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei
  1 +<#include "/common/utils.ftl">
1 2 <template>
2   - <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  3 + <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="${getModalWidth(tableVo.fieldRowNum?default(1))}" @ok="handleSubmit">
3 4 <BasicForm @register="registerForm"/>
4 5 </BasicModal>
5 6 </template>
... ... @@ -18,6 +19,7 @@
18 19 labelWidth: 150,
19 20 schemas: formSchema,
20 21 showActionButtonGroup: false,
  22 + baseColProps: {span: ${getFormSpan(tableVo.fieldRowNum?default(1))}}
21 23 });
22 24 //表单赋值
23 25 const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
... ... @@ -54,5 +56,12 @@
54 56 </script>
55 57  
56 58 <style lang="less" scoped>
  59 + /** 时间和数字输入框样式 */
  60 + :deep(.ant-input-number){
  61 + width: 100%
  62 + }
57 63  
  64 + :deep(.ant-calendar-picker){
  65 + width: 100%
  66 + }
58 67 </style>
59 68 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 +<#assign query_field_no=0>
  4 +<#assign need_category = false>
  5 +<#assign need_pca = false>
  6 +<#assign need_search = false>
  7 +<#assign need_dept_user = false>
  8 +<#assign need_switch = false>
  9 +<#assign need_dept = false>
  10 +<#assign need_multi = false>
  11 +<#assign need_popup = false>
  12 +<#assign need_select_tag = false>
  13 +<#assign need_select_tree = false>
  14 +<#assign need_time = false>
  15 +<#assign bpm_flag=false>
  16 +<#assign need_markdown = false>
  17 +<#assign need_upload = false>
  18 +<#assign need_image_upload = false>
  19 +<#assign need_editor = false>
  20 +<#assign need_checkbox = false>
  21 +<#assign query_flag = false>
  22 + <!--查询区域-->
  23 + <div class="jeecg-basic-table-form-container">
  24 + <a-form @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
  25 + <a-row :gutter="24">
  26 +<#-- 开始循环 -->
  27 +<#list columns as po>
  28 +<#if po.fieldDbName=='bpm_status'>
  29 + <#assign bpm_flag=true>
  30 +</#if>
  31 +<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
  32 +<#assign need_category=true>
  33 +</#if>
  34 +<#if po.classType=='pca'>
  35 +<#assign need_pca=true>
  36 +</#if>
  37 +<#if po.classType=='sel_search'>
  38 +<#assign need_search = true>
  39 +</#if>
  40 +<#if po.classType=='sel_user'>
  41 +<#assign need_dept_user = true>
  42 +</#if>
  43 +<#if po.classType=='sel_depart'>
  44 +<#assign need_dept = true>
  45 +</#if>
  46 +<#if po.classType=='switch'>
  47 +<#assign need_switch = true>
  48 +</#if>
  49 +<#if po.classType=='list_multi'>
  50 +<#assign need_multi = true>
  51 +</#if>
  52 +<#if po.classType=='popup'>
  53 +<#assign need_popup = true>
  54 +</#if>
  55 +<#if po.classType=='sel_tree'>
  56 +<#assign need_select_tree = true>
  57 +</#if>
  58 +<#if po.classType=='time'>
  59 +<#assign need_time = true>
  60 +</#if>
  61 + <#include "/common/form/native/vue3NativeSearch.ftl">
  62 +</#list>
  63 +<#if query_field_no gt 2>
  64 + </template>
  65 +</#if>
  66 +<#if query_flag>
  67 + <a-col :xl="6" :lg="7" :md="8" :sm="24">
  68 + <span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
  69 + <a-col :lg="6">
  70 + <a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery">查询</a-button>
  71 + <a-button type="primary" preIcon="ant-design:reload-outlined" @click="searchReset" style="margin-left: 8px">重置</a-button>
  72 + <a @click="toggleSearchStatus = !toggleSearchStatus" style="margin-left: 8px">
  73 + {{ toggleSearchStatus ? '收起' : '展开' }}
  74 + <Icon :icon="toggleSearchStatus ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
  75 + </a>
  76 + </a-col>
  77 + </span>
  78 + </a-col>
  79 +</#if>
  80 + </a-row>
  81 + </a-form>
  82 + </div>
  83 +<#-- 结束循环 -->
  84 + <!--引用表格-->
  85 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  86 + <!--插槽:table标题-->
  87 + <template #tableTitle>
  88 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  89 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  90 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  91 + <a-dropdown v-if="selectedRowKeys.length > 0">
  92 + <template #overlay>
  93 + <a-menu>
  94 + <a-menu-item key="1" @click="batchHandleDelete">
  95 + <Icon icon="ant-design:delete-outlined"></Icon>
  96 + 删除
  97 + </a-menu-item>
  98 + </a-menu>
  99 + </template>
  100 + <a-button>批量操作
  101 + <Icon icon="mdi:chevron-down"></Icon>
  102 + </a-button>
  103 + </a-dropdown>
  104 + </template>
  105 + <!--操作栏-->
  106 + <template #action="{ record }">
  107 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  108 + </template>
  109 + <!--字段回显插槽-->
  110 + <template #htmlSlot="{text}">
  111 + <div v-html="text"></div>
  112 + </template>
  113 + <!--省市区字段回显插槽-->
  114 + <template #pcaSlot="{text}">
  115 + {{ getAreaTextByCode(text) }}
  116 + </template>
  117 + <template #fileSlot="{text}">
  118 + <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
  119 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  120 + </template>
  121 + </BasicTable>
  122 + <!-- 表单区域 -->
  123 + <${entityName}Modal ref="registerModal" @success="handleSuccess"></${entityName}Modal>
  124 + </div>
  125 +</template>
  126 +
  127 +<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
  128 + import { ref, reactive } from 'vue';
  129 + import { BasicTable, useTable, TableAction } from '/@/components/Table';
  130 + import { useListPage } from '/@/hooks/system/useListPage';
  131 + import { columns } from './${entityName}.data';
  132 + import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './${entityName}.api';
  133 + import { downloadFile } from '/@/utils/common/renderUtils';
  134 + import ${entityName}Modal from './components/${entityName}Modal.vue'
  135 + <#include "/common/form/native/vue3NativeImport.ftl">
  136 +<#if need_category>
  137 + import { loadCategoryData } from '/@/api/common/api';
  138 + import { getAuthCache, setAuthCache } from '/@/utils/auth';
  139 + import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
  140 +</#if>
  141 +<#if need_pca>
  142 + import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
  143 +</#if>
  144 +
  145 + const queryParam = ref<any>({});
  146 + const toggleSearchStatus = ref<boolean>(false);
  147 + const registerModal = ref();
  148 + //注册table数据
  149 + const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
  150 + tableProps: {
  151 + title: '${tableVo.ftlDescription}',
  152 + api: list,
  153 + columns,
  154 + canResize:false,
  155 + useSearchForm: false,
  156 + actionColumn: {
  157 + width: 120,
  158 + fixed: 'right',
  159 + },
  160 + beforeFetch: (params) => {
  161 + return Object.assign(params, queryParam.value);
  162 + },
  163 + },
  164 + exportConfig: {
  165 + name: "${tableVo.ftlDescription}",
  166 + url: getExportUrl,
  167 + },
  168 + importConfig: {
  169 + url: getImportUrl,
  170 + success: handleSuccess
  171 + },
  172 + });
  173 + const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
  174 + const labelCol = reactive({
  175 + xs: { span: 24 },
  176 + sm: { span: 7 },
  177 + });
  178 + const wrapperCol = reactive({
  179 + xs: { span: 24 },
  180 + sm: { span: 16 },
  181 + });
  182 +
  183 + /**
  184 + * 新增事件
  185 + */
  186 + function handleAdd() {
  187 + registerModal.value.disableSubmit = false;
  188 + registerModal.value.add();
  189 + }
  190 +
  191 + /**
  192 + * 编辑事件
  193 + */
  194 + function handleEdit(record: Recordable) {
  195 + registerModal.value.disableSubmit = false;
  196 + registerModal.value.edit(record);
  197 + }
  198 +
  199 + /**
  200 + * 详情
  201 + */
  202 + function handleDetail(record: Recordable) {
  203 + registerModal.value.disableSubmit = true;
  204 + registerModal.value.edit(record);
  205 + }
  206 +
  207 + /**
  208 + * 删除事件
  209 + */
  210 + async function handleDelete(record) {
  211 + await deleteOne({ id: record.id }, handleSuccess);
  212 + }
  213 +
  214 + /**
  215 + * 批量删除事件
  216 + */
  217 + async function batchHandleDelete() {
  218 + await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
  219 + }
  220 +
  221 + /**
  222 + * 成功回调
  223 + */
  224 + function handleSuccess() {
  225 + (selectedRowKeys.value = []) && reload();
  226 + }
  227 +
  228 + /**
  229 + * 操作栏
  230 + */
  231 + function getTableAction(record) {
  232 + return [
  233 + {
  234 + label: '编辑',
  235 + onClick: handleEdit.bind(null, record),
  236 + },
  237 + ];
  238 + }
  239 +
  240 + /**
  241 + * 下拉操作栏
  242 + */
  243 + function getDropDownAction(record) {
  244 + return [
  245 + {
  246 + label: '详情',
  247 + onClick: handleDetail.bind(null, record),
  248 + },
  249 + {
  250 + label: '删除',
  251 + popConfirm: {
  252 + title: '是否确认删除',
  253 + confirm: handleDelete.bind(null, record),
  254 + },
  255 + },
  256 + ];
  257 + }
  258 +
  259 + /**
  260 + * 查询
  261 + */
  262 + function searchQuery() {
  263 + reload();
  264 + }
  265 +
  266 + /**
  267 + * 重置
  268 + */
  269 + function searchReset() {
  270 + queryParam.value = {};
  271 + selectedRowKeys.value = [];
  272 + //刷新数据
  273 + reload();
  274 + }
  275 +
  276 + <#if need_popup>
  277 + /**
  278 + * popup组件值改变事件
  279 + */
  280 + function setFieldsValue(map) {
  281 + Object.keys(map).map((key) => {
  282 + queryParam.value[key] = map[key];
  283 + });
  284 + }
  285 + </#if>
  286 +
  287 + <#if need_pca>
  288 + /**
  289 + * 省市区点击事件
  290 + * @param key
  291 + * @param value
  292 + */
  293 + function handleAreaChange(key, value) {
  294 + queryParam.value[key] = value.join(',');
  295 + }
  296 + </#if>
  297 +
  298 + <#if need_category>
  299 + /**
  300 + * form点击事件
  301 + * @param value
  302 + */
  303 + function handleFormChange(key, value) {
  304 + queryParam.value[key] = value;
  305 + }
  306 +
  307 + /**
  308 + * 初始化字典配置
  309 + */
  310 + function initDictConfig() {
  311 + <#list columns as po>
  312 + <#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
  313 + <#if po.classType=='cat_tree' && need_category==true>
  314 + loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
  315 + if (res) {
  316 + let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
  317 + if(!allDictDate['${po.dictField?default("")}']){
  318 + Object.assign(allDictDate,{'${po.dictField?default("")}':res})
  319 + }
  320 + setAuthCache(DB_DICT_DATA_KEY,allDictDate)
  321 + }
  322 + });
  323 + </#if>
  324 + </#if>
  325 + </#list>
  326 + }
  327 + initDictConfig();
  328 + </#if>
  329 +</script>
  330 +
  331 +<style lang="less" scoped>
  332 + .jeecg-basic-table-form-container {
  333 + .table-page-search-submitButtons {
  334 + display: block;
  335 + margin-bottom: 24px;
  336 + white-space: nowrap;
  337 + }
  338 + .query-group-cust{
  339 + width: calc(50% - 15px);
  340 + min-width: 100px !important;
  341 + }
  342 + .query-group-split-cust{
  343 + width: 30px;
  344 + display: inline-block;
  345 + text-align: center
  346 + }
  347 + }
  348 +</style>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}__api.tsi 0 → 100644
  1 +import { defHttp } from '/@/utils/http/axios';
  2 +import { Modal } from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 +}
  13 +
  14 +/**
  15 + * 导出api
  16 + * @param params
  17 + */
  18 +export const getExportUrl = Api.exportXls;
  19 +
  20 +/**
  21 + * 导入api
  22 + */
  23 +export const getImportUrl = Api.importExcel;
  24 +
  25 +/**
  26 + * 列表接口
  27 + * @param params
  28 + */
  29 +export const list = (params) => defHttp.get({ url: Api.list, params });
  30 +
  31 +/**
  32 + * 删除单个
  33 + * @param params
  34 + * @param handleSuccess
  35 + */
  36 +export const deleteOne = (params,handleSuccess) => {
  37 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  38 + handleSuccess();
  39 + });
  40 +}
  41 +
  42 +/**
  43 + * 批量删除
  44 + * @param params
  45 + * @param handleSuccess
  46 + */
  47 +export const batchDelete = (params, handleSuccess) => {
  48 + Modal.confirm({
  49 + title: '确认删除',
  50 + content: '是否删除选中数据',
  51 + okText: '确认',
  52 + cancelText: '取消',
  53 + onOk: () => {
  54 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  55 + handleSuccess();
  56 + });
  57 + }
  58 + });
  59 +}
  60 +
  61 +/**
  62 + * 保存或者更新
  63 + * @param params
  64 + * @param isUpdate
  65 + */
  66 +export const saveOrUpdate = (params, isUpdate) => {
  67 + let url = isUpdate ? Api.edit : Api.save;
  68 + return defHttp.post({ url: url, params }, { isTransformResponse: false });
  69 +}
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}__data.tsi 0 → 100644
  1 +<#include "/common/utils.ftl">
  2 +import {BasicColumn} from '/@/components/Table';
  3 +import {FormSchema} from '/@/components/Table';
  4 +import { rules} from '/@/utils/helper/validator';
  5 +import { render } from '/@/utils/common/renderUtils';
  6 +//列表数据
  7 +export const columns: BasicColumn[] = [
  8 + <#list columns as po>
  9 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  10 + {
  11 + title: '${po.filedComment}',
  12 + align: "center",
  13 + <#if po.sort=='Y'>
  14 + sorter: true,
  15 + </#if>
  16 + <#if po.classType=='date'>
  17 + dataIndex: '${po.fieldName}',
  18 + customRender:({text}) =>{
  19 + return !text?"":(text.length>10?text.substr(0,10):text);
  20 + },
  21 + <#elseif po.fieldDbType=='Blob'>
  22 + dataIndex: '${po.fieldName}String'
  23 + <#elseif po.classType=='umeditor'>
  24 + dataIndex: '${po.fieldName}',
  25 + slots: { customRender: 'htmlSlot' },
  26 + <#elseif po.classType=='pca'>
  27 + dataIndex: '${po.fieldName}',
  28 + slots: { customRender: 'pcaSlot' },
  29 + <#elseif po.classType=='file'>
  30 + dataIndex: '${po.fieldName}',
  31 + slots: { customRender: 'fileSlot' },
  32 + <#elseif po.classType=='image'>
  33 + dataIndex: '${po.fieldName}',
  34 + customRender: render.renderImage,
  35 + <#elseif po.classType=='switch'>
  36 + dataIndex: '${po.fieldName}',
  37 +<#assign switch_extend_arr=['Y','N']>
  38 +<#if po.dictField?default("")?contains("[")>
  39 +<#assign switch_extend_arr=po.dictField?eval>
  40 +</#if>
  41 +<#list switch_extend_arr as a>
  42 +<#if a_index == 0>
  43 +<#assign switch_extend_arr1=a>
  44 +<#else>
  45 +<#assign switch_extend_arr2=a>
  46 +</#if>
  47 +</#list>
  48 + customRender:({text}) => {
  49 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}]);
  50 + },
  51 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  52 + dataIndex: '${po.fieldName}_dictText'
  53 + <#elseif po.classType=='cat_tree'>
  54 + dataIndex: '${po.fieldName}',
  55 + <#if po.dictText?default("")?trim?length == 0>
  56 + customRender:({text}) => {
  57 + return render.renderCategoryTree(text,'${po.dictField?default("")}');
  58 + },
  59 + <#else>
  60 + customRender: ({text, record}) => (text ? record['${po.dictText}'] : '');
  61 + </#if>
  62 + <#else>
  63 + dataIndex: '${po.fieldName}'
  64 + </#if>
  65 + },
  66 + </#if>
  67 + </#list>
  68 +];
  69 +
  70 +//查询数据
  71 +export const searchFormSchema: FormSchema[] = [
  72 +<#-- 开始循环 -->
  73 +<#list columns as po>
  74 +<#if po.fieldDbName=='bpm_status'>
  75 + <#assign bpm_flag=true>
  76 +</#if>
  77 +<#if po.isQuery=='Y'>
  78 +<#assign query_flag=true>
  79 + <#assign query_field_dictCode="">
  80 + <#if po.dictTable?default("")?trim?length gt 1>
  81 + <#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  82 + <#elseif po.dictField?default("")?trim?length gt 1>
  83 + <#assign query_field_dictCode="${po.dictField}">
  84 + </#if>
  85 +<#if po.queryMode=='single'>
  86 + {
  87 + label: "${po.filedComment}",
  88 + field: ${autoStringSuffix(po)},
  89 +<#if po.classType=='sel_search'>
  90 + component: 'JSearchSelect',
  91 + componentProps:{
  92 + dict: "${po.dictTable},${po.dictText},${po.dictField}"
  93 + },
  94 +<#elseif po.classType=='sel_user'>
  95 + component: 'JSelectUserByDept',
  96 +<#elseif po.classType=='switch'>
  97 + component: 'JSwitch',
  98 + componentProps:{
  99 + <#if po.dictField != 'is_open'>
  100 + options: "${po.dictField}"
  101 + </#if>
  102 + },
  103 + <#elseif po.classType=='sel_depart'>
  104 + component: 'JSelectDept',
  105 + <#elseif po.classType=='list_multi'>
  106 + component: 'JSelectMultiple',
  107 + componentProps: {
  108 + <#if po.dictTable?default("")?trim?length gt 1>
  109 + dictCode: "${po.dictTable},${po.dictText},${po.dictField}"
  110 + <#elseif po.dictField?default("")?trim?length gt 1>
  111 + dictCode: "${po.dictField}"
  112 + </#if>
  113 + },
  114 + <#elseif po.classType=='cat_tree'>
  115 + component: 'JCategorySelect',
  116 + componentProps:{
  117 + pcode: "${po.dictField?default("")}",//back和事件未添加,暂时有问题
  118 + },
  119 +<#elseif po.classType=='date'>
  120 + component: 'DatePicker',
  121 +<#elseif po.classType=='datetime'>
  122 + component: 'DatePicker',
  123 + componentProps: {
  124 + showTime: true,
  125 + },
  126 + <#elseif po.classType =='time'>
  127 + component: 'TimePicker',
  128 + componentProps: {
  129 + valueFormat: 'HH:mm:ss',
  130 + },
  131 +<#elseif po.classType=='pca'>
  132 + component: 'JAreaLinkage',
  133 +<#elseif po.classType=='popup'>
  134 + <#include "/common/form/vue3popup.ftl">
  135 +<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  136 +<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
  137 + component: 'JDictSelectTag',
  138 + componentProps:{
  139 + <#if po.dictTable?default("")?trim?length gt 1>
  140 + dictCode: "${po.dictTable},${po.dictText},${po.dictField}"
  141 + <#elseif po.dictField?default("")?trim?length gt 1>
  142 + dictCode: "${po.dictField}"
  143 + </#if>
  144 + },
  145 +<#else>
  146 + component: 'Input',
  147 +</#if>
  148 + colProps: {span: 6},
  149 + },
  150 +<#else>
  151 + {
  152 + label: "${po.filedComment}",
  153 + field: "${po.fieldName}",
  154 +<#if po.classType=='date'>
  155 + component: 'RangePicker',
  156 +<#elseif po.classType=='datetime'>
  157 + component: 'RangePicker',
  158 + componentProps: {
  159 + showTime: true,
  160 + },
  161 +<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  162 + component: 'JRangeNumber',
  163 +<#else>
  164 + component: 'Input', //TODO 范围查询
  165 +</#if>
  166 + colProps: {span: 6},
  167 + },
  168 +</#if>
  169 +</#if>
  170 +</#list>
  171 +<#-- 结束循环 -->
  172 +];
  173 +
  174 +//表单数据
  175 +export const formSchema: FormSchema[] = [
  176 +<#assign form_cat_tree = false>
  177 +<#assign form_cat_back = "">
  178 +<#assign bpm_flag=false>
  179 +<#assign id_exists = false>
  180 +<#list columns as po><#rt/>
  181 +<#if po.fieldDbName=='bpm_status'>
  182 + <#assign bpm_flag=true>
  183 +</#if>
  184 +<#if po.fieldDbName == 'id'>
  185 + <#assign id_exists = true>
  186 +</#if>
  187 +<#if po.isShow =='Y'>
  188 +<#assign form_field_dictCode="">
  189 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  190 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  191 + <#elseif po.dictField?default("")?trim?length gt 1>
  192 + <#assign form_field_dictCode="${po.dictField}">
  193 + </#if>
  194 + {
  195 + label: '${po.filedComment}',
  196 + field: ${autoStringSuffix(po)},
  197 + <#if po.classType =='date'>
  198 + component: 'DatePicker',
  199 + <#elseif po.classType =='datetime'>
  200 + component: 'DatePicker',
  201 + componentProps: {
  202 + showTime: true,
  203 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
  204 + },
  205 + <#elseif po.classType =='time'>
  206 + component: 'TimePicker',
  207 + componentProps: {
  208 + valueFormat: 'HH:mm:ss'
  209 + },
  210 + <#elseif po.classType =='popup'>
  211 + <#include "/common/form/vue3popup.ftl">
  212 + <#elseif po.classType =='sel_depart'>
  213 + component: 'JSelectDept',
  214 + <#elseif po.classType =='switch'>
  215 + component: 'JSwitch',
  216 + componentProps:{
  217 + <#if po.dictField != 'is_open'>
  218 + options: ${po.dictField}
  219 + </#if>
  220 + },
  221 + <#elseif po.classType =='pca'>
  222 + component: 'JAreaLinkage',
  223 + <#elseif po.classType =='markdown'>
  224 + component: 'JMarkdownEditor',//注意string转换问题
  225 + <#elseif po.classType =='password'>
  226 + component: 'InputPassword',
  227 + <#elseif po.classType =='sel_user'>
  228 + component: 'JSelectUserByDept',
  229 + componentProps:{
  230 + labelKey: 'realname',
  231 + },
  232 + <#elseif po.classType =='textarea'>
  233 + component: 'InputTextArea',
  234 + <#elseif po.classType=='list' || po.classType=='radio'>
  235 + component: 'JDictSelectTag',
  236 + componentProps:{
  237 + dictCode: "${form_field_dictCode}"
  238 + },
  239 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  240 + component: 'JSelectMultiple',
  241 + componentProps:{
  242 + dictCode: "${form_field_dictCode}"
  243 + },
  244 + <#elseif po.classType=='sel_search'>
  245 + component: 'JSearchSelect',
  246 + componentProps:{
  247 + dict: "${form_field_dictCode}"
  248 + },
  249 +<#elseif po.classType=='cat_tree'>
  250 + <#assign form_cat_tree = true>
  251 + component: 'JCategorySelect',
  252 + componentProps:{
  253 + pcode: "${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  254 + },
  255 + <#if po.dictText?default("")?trim?length gt 1>
  256 + <#assign form_cat_back = "${po.dictText}">
  257 + </#if>
  258 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  259 + component: 'InputNumber',
  260 + <#elseif po.classType=='file'>
  261 + component: 'JUpload',
  262 + componentProps:{
  263 + <#if po.uploadnum??>
  264 + maxCount: ${po.uploadnum}
  265 + </#if>
  266 + },
  267 + <#elseif po.classType=='image'>
  268 + component: 'JImageUpload',
  269 + componentProps:{
  270 + <#if po.uploadnum??>
  271 + fileMax: ${po.uploadnum}
  272 + </#if>
  273 + },
  274 + <#elseif po.classType=='umeditor'>
  275 + component: 'JEditor',
  276 + <#elseif po.classType == 'sel_tree'>
  277 + component: 'JTreeSelect',
  278 + componentProps:{
  279 + <#if po.dictText??>
  280 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  281 + dict: "${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  282 + <#elseif po.dictText?split(',')[1]??>
  283 + pidField: "${po.dictText?split(',')[1]}",
  284 + <#elseif po.dictText?split(',')[3]??>
  285 + hasChildField: "${po.dictText?split(',')[3]}",
  286 + </#if>
  287 + </#if>
  288 + pidValue: "${po.dictField}",
  289 + },
  290 + <#else>
  291 + component: 'Input',
  292 + </#if>
  293 + <#if po.isShow == 'Y' && poHasCheck(po)>
  294 + dynamicRules: ({model,schema}) => {
  295 + <#if po.fieldName != 'id'>
  296 + <#assign fieldValidType = po.fieldValidType!''>
  297 + return [
  298 + <#-- 非空校验 -->
  299 + <#if po.nullable == 'N' || fieldValidType == '*'>
  300 + { required: true, message: '请输入${po.filedComment}!'},
  301 + <#elseif fieldValidType!=''>
  302 + { required: false},
  303 + </#if>
  304 + <#-- 唯一校验 -->
  305 + <#if fieldValidType == 'only'>
  306 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
  307 + <#-- 6到16位数字 -->
  308 + <#elseif fieldValidType == 'n6-16'>
  309 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  310 + <#-- 6到16位任意字符 -->
  311 + <#elseif fieldValidType == '*6-16'>
  312 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  313 + <#-- 6到18位字符串 -->
  314 + <#elseif fieldValidType == 's6-18'>
  315 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  316 + <#-- 网址 -->
  317 + <#elseif fieldValidType == 'url'>
  318 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  319 + <#-- 电子邮件 -->
  320 + <#elseif fieldValidType == 'e'>
  321 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  322 + <#-- 手机号码 -->
  323 + <#elseif fieldValidType == 'm'>
  324 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  325 + <#-- 邮政编码 -->
  326 + <#elseif fieldValidType == 'p'>
  327 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  328 + <#-- 字母 -->
  329 + <#elseif fieldValidType == 's'>
  330 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  331 + <#-- 数字 -->
  332 + <#elseif fieldValidType == 'n'>
  333 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  334 + <#-- 整数 -->
  335 + <#elseif fieldValidType == 'z'>
  336 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  337 + <#-- 金额 -->
  338 + <#elseif fieldValidType == 'money'>
  339 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  340 + <#-- 正则校验 -->
  341 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  342 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  343 + <#-- 无校验 -->
  344 + <#else>
  345 + <#t>
  346 + </#if>
  347 + ];
  348 + </#if>
  349 + },
  350 + </#if>
  351 + <#if po.readonly=='Y'>
  352 + dynamicDisabled: true
  353 + </#if>
  354 + },
  355 +</#if>
  356 +</#list>
  357 +<#if id_exists == false>
  358 + // TODO 主键隐藏字段,目前写死为ID
  359 + {
  360 + label: '',
  361 + field: 'id',
  362 + component: 'Input',
  363 + show: false,
  364 + },
  365 +</#if>
  366 +];
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3Native/components/${entityName}Form.vuei 0 → 100644
  1 +<#include "/common/utils.ftl">
  2 +<template>
  3 + <a-spin :spinning="confirmLoading">
  4 + <a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
  5 + <a-row>
  6 +<#assign need_category = false>
  7 +<#assign bpm_flag=false>
  8 +<#assign need_pca = false>
  9 +<#assign need_search = false>
  10 +<#assign need_dept_user = false>
  11 +<#assign need_switch = false>
  12 +<#assign need_dept = false>
  13 +<#assign need_multi = false>
  14 +<#assign need_popup = false>
  15 +<#assign need_select_tag = false>
  16 +<#assign need_select_tree = false>
  17 +<#assign need_time = false>
  18 +<#assign need_markdown = false>
  19 +<#assign need_upload = false>
  20 +<#assign need_image_upload = false>
  21 +<#assign need_editor = false>
  22 +<#assign need_checkbox = false>
  23 +<#assign hasOnlyValidate = false>
  24 +<#assign form_span = 24>
  25 +<#if tableVo.fieldRowNum==2>
  26 +<#assign form_span = 12>
  27 +<#elseif tableVo.fieldRowNum==3>
  28 +<#assign form_span = 8>
  29 +<#elseif tableVo.fieldRowNum==4>
  30 +<#assign form_span = 6>
  31 +</#if>
  32 +<#list columns as po>
  33 +<#if po.fieldDbName=='bpm_status'>
  34 + <#assign bpm_flag=true>
  35 +</#if>
  36 +<#if po.isShow == 'Y' && po.fieldValidType?default("") == 'only'>
  37 + <#assign hasOnlyValidate = true>
  38 +</#if>
  39 + <#include "/common/form/native/vue3NativeForm.ftl">
  40 +</#list>
  41 + </a-row>
  42 + </a-form>
  43 + </a-spin>
  44 +</template>
  45 +
  46 +<script lang="ts" setup>
  47 + import { ref, reactive, defineExpose, nextTick, defineProps, computed } from 'vue';
  48 + import { defHttp } from '/@/utils/http/axios';
  49 + import { useMessage } from '/@/hooks/web/useMessage';
  50 + import moment from 'moment';
  51 + <#include "/common/form/native/vue3NativeImport.ftl">
  52 + import { getValueType } from '/@/utils';
  53 + import { saveOrUpdate } from '../${entityName}.api';
  54 + import { Form } from 'ant-design-vue';
  55 + <#if hasOnlyValidate == true>
  56 + import { duplicateValidate } from '/@/utils/helper/validator'
  57 + </#if>
  58 +
  59 + const props = defineProps({
  60 + disabled: { type: Boolean, default: false },
  61 + });
  62 + const formRef = ref();
  63 + const useForm = Form.useForm;
  64 + const emit = defineEmits(['register', 'ok']);
  65 + const formData = reactive<Record<string, any>>({
  66 +<#list columns as po>
  67 + <#if po.isShow == 'Y'>
  68 + <#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  69 + ${po.fieldName}: undefined,
  70 + <#elseif po.fieldDbType=='Blob'>
  71 + ${po.fieldName}String: '',
  72 + <#else>
  73 + ${po.fieldName}: '',
  74 + </#if>
  75 + </#if>
  76 +</#list>
  77 + });
  78 + const { createMessage } = useMessage();
  79 + const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
  80 + const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
  81 + const confirmLoading = ref<boolean>(false);
  82 + //表单验证
  83 + const validatorRules = {
  84 + <#include "/common/validatorRulesTemplate/native/vue3MainNative.ftl">
  85 + };
  86 + const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: true });
  87 +
  88 + /**
  89 + * 新增
  90 + */
  91 + function add() {
  92 + edit({});
  93 + }
  94 +
  95 + /**
  96 + * 编辑
  97 + */
  98 + function edit(record) {
  99 + nextTick(() => {
  100 + resetFields();
  101 + //赋值
  102 + Object.assign(formData, record);
  103 + });
  104 + }
  105 +
  106 + /**
  107 + * 提交数据
  108 + */
  109 + async function submitForm() {
  110 + // 触发表单验证
  111 + await validate();
  112 + confirmLoading.value = true;
  113 + const isUpdate = ref<boolean>(false);
  114 + //时间格式化
  115 + let model = formData;
  116 + if (model.id) {
  117 + isUpdate.value = true;
  118 + }
  119 + //循环数据
  120 + for (let data in model) {
  121 + //如果该数据是数组并且是字符串类型
  122 + if (model[data] instanceof Array) {
  123 + let valueType = getValueType(formRef.value.getProps, data);
  124 + //如果是字符串类型的需要变成以逗号分割的字符串
  125 + if (valueType === 'string') {
  126 + model[data] = model[data].join(',');
  127 + }
  128 + }
  129 + }
  130 + await saveOrUpdate(model, isUpdate.value)
  131 + .then((res) => {
  132 + if (res.success) {
  133 + createMessage.success(res.message);
  134 + emit('ok');
  135 + } else {
  136 + createMessage.warning(res.message);
  137 + }
  138 + })
  139 + .finally(() => {
  140 + confirmLoading.value = false;
  141 + });
  142 + }
  143 +
  144 + <#if need_popup>
  145 + /**
  146 + * popup组件值改变事件
  147 + */
  148 + function setFieldsValue(map) {
  149 + Object.keys(map).map((key) => {
  150 + formData[key] = map[key];
  151 + });
  152 + }
  153 + </#if>
  154 +
  155 + <#if need_category || need_select_tree>
  156 + /**
  157 + * 值改变事件触发
  158 + * @param key
  159 + * @param value
  160 + */
  161 + function handleFormChange(key, value) {
  162 + formData[key] = value;
  163 + }
  164 + </#if>
  165 + <#list columns as po>
  166 + <#if po.isShow == 'Y' && po.fieldValidType?default("") == 'only'>
  167 + async function ${po.fieldName}Duplicatevalidate(_r, value) {
  168 + return duplicateValidate('${tableName}', '${po.fieldDbName}', value, formData.id || '')
  169 + }
  170 + </#if>
  171 + </#list>
  172 + defineExpose({
  173 + add,
  174 + edit,
  175 + submitForm,
  176 + });
  177 +</script>
  178 +
  179 +<style lang="less" scoped>
  180 + .antd-modal-form {
  181 + height: 500px !important;
  182 + overflow-y: auto;
  183 + padding: 24px 24px 24px 24px;
  184 + }
  185 +</style>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3Native/components/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <a-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
  3 + <${entityName}Form ref="registerForm" @ok="submitCallback" :disabled="disableSubmit"></${entityName}Form>
  4 + </a-modal>
  5 +</template>
  6 +
  7 +<script lang="ts" setup>
  8 + import { ref, nextTick, defineExpose } from 'vue';
  9 + import ${entityName}Form from './${entityName}Form.vue'
  10 +
  11 + const title = ref<string>('');
  12 + const width = ref<number>(800);
  13 + const visible = ref<boolean>(false);
  14 + const disableSubmit = ref<boolean>(false);
  15 + const registerForm = ref();
  16 + const emit = defineEmits(['register', 'success']);
  17 +
  18 + /**
  19 + * 新增
  20 + */
  21 + function add() {
  22 + title.value = '新增';
  23 + visible.value = true;
  24 + nextTick(() => {
  25 + registerForm.value.add();
  26 + });
  27 + }
  28 +
  29 + /**
  30 + * 编辑
  31 + * @param record
  32 + */
  33 + function edit(record) {
  34 + title.value = disableSubmit.value ? '详情' : '编辑';
  35 + visible.value = true;
  36 + nextTick(() => {
  37 + registerForm.value.edit(record);
  38 + });
  39 + }
  40 +
  41 + /**
  42 + * 确定按钮点击事件
  43 + */
  44 + function handleOk() {
  45 + registerForm.value.submitForm();
  46 + }
  47 +
  48 + /**
  49 + * form保存回调事件
  50 + */
  51 + function submitCallback() {
  52 + handleCancel();
  53 + emit('success');
  54 + }
  55 +
  56 + /**
  57 + * 取消按钮回调事件
  58 + */
  59 + function handleCancel() {
  60 + visible.value = false;
  61 + }
  62 +
  63 + defineExpose({
  64 + add,
  65 + edit,
  66 + disableSubmit,
  67 + });
  68 +</script>
  69 +
  70 +<style>
  71 + /**隐藏样式-modal确定按钮 */
  72 + .jee-hidden {
  73 + display: none !important;
  74 + }
  75 +</style>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -205,17 +205,14 @@ public class ${entityName}Controller {
205 205 QueryWrapper<${entityName}> queryWrapper = QueryGenerator.initQueryWrapper(${entityName?uncap_first}, request.getParameterMap());
206 206 LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
207 207  
208   - //Step.2 获取导出数据
209   - List<${entityName}> queryList = ${entityName?uncap_first}Service.list(queryWrapper);
210   - // 过滤选中数据
  208 + //配置选中数据查询条件
211 209 String selections = request.getParameter("selections");
212   - List<${entityName}> ${entityName?uncap_first}List = new ArrayList<${entityName}>();
213   - if(oConvertUtils.isEmpty(selections)) {
214   - ${entityName?uncap_first}List = queryList;
215   - }else {
  210 + if(oConvertUtils.isNotEmpty(selections)) {
216 211 List<String> selectionList = Arrays.asList(selections.split(","));
217   - ${entityName?uncap_first}List = queryList.stream().filter(item -> selectionList.contains(item.getId())).collect(Collectors.toList());
  212 + queryWrapper.in("id",selectionList);
218 213 }
  214 + //Step.2 获取导出数据
  215 + List<${entityName}> ${entityName?uncap_first}List = ${entityName?uncap_first}Service.list(queryWrapper);
219 216  
220 217 // Step.3 组装pageList
221 218 List<${entityName}Page> pageList = new ArrayList<${entityName}Page>();
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/onetomany/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
  1 +<#include "/common/utils.ftl">
1 2 package ${bussiPackage}.${entityPackage}.entity;
2 3  
3 4 import java.io.Serializable;
... ... @@ -31,9 +32,9 @@ public class ${entityName} implements Serializable {
31 32 <#-- 生成字典Code -->
32 33 <#assign list_field_dictCode="">
33 34 <#if po.classType='sel_user'>
34   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  35 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
35 36 <#elseif po.classType='sel_depart'>
36   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  37 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
37 38 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
38 39 <#if po.dictTable?default("")?trim?length gt 1>
39 40 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/onetomany/java/${bussiPackage}/${entityPackage}/entity/[1-n]Entity.javai
  1 +<#include "/common/utils.ftl">
1 2 <#list subTables as subTab>
2 3 #segment#${subTab.entityName}.java
3 4 package ${bussiPackage}.${entityPackage}.entity;
... ... @@ -32,9 +33,9 @@ public class ${subTab.entityName} implements Serializable {
32 33 <#-- 生成字典Code -->
33 34 <#assign list_field_dictCode="">
34 35 <#if po.classType='sel_user'>
35   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  36 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
36 37 <#elseif po.classType='sel_depart'>
37   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  38 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
38 39 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
39 40 <#if po.dictTable?default("")?trim?length gt 1>
40 41 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/onetomany/java/${bussiPackage}/${entityPackage}/vue/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Form.vuei
... ... @@ -48,7 +48,7 @@
48 48 @input="popupCallback"
49 49 <#if po.readonly=='Y'>disabled</#if>/>
50 50 <#elseif po.classType =='sel_depart'>
51   - <j-select-depart v-model="model.${po.fieldName}" multi <#if po.readonly=='Y'>disabled</#if>/>
  51 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
52 52 <#elseif po.classType =='switch'>
53 53 <j-switch v-model="model.${po.fieldName}" <#if po.dictField != 'is_open'>:options="${po.dictField}"</#if> <#if po.readonly=='Y'>disabled</#if>></j-switch>
54 54 <#elseif po.classType =='pca'>
... ... @@ -58,7 +58,7 @@
58 58 <#elseif po.classType =='password'>
59 59 <a-input-password v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
60 60 <#elseif po.classType =='sel_user'>
61   - <j-select-user-by-dep v-model="model.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if>/>
  61 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
62 62 <#elseif po.classType =='textarea'>
63 63 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
64 64 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/[1-n]Form.vuei
... ... @@ -44,7 +44,7 @@
44 44 :multi="${po.extendParams.popupMulti?c}"
45 45 @input="popupCallback"/>
46 46 <#elseif po.classType =='sel_depart'>
47   - <j-select-depart v-model="model.${po.fieldName}" multi/>
  47 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
48 48 <#elseif po.classType =='switch'>
49 49 <j-switch v-model="model.${po.fieldName}" <#if po.dictField!= 'is_open'>:options="${po.dictField}"</#if>></j-switch>
50 50 <#elseif po.classType =='pca'>
... ... @@ -54,7 +54,7 @@
54 54 <#elseif po.classType =='password'>
55 55 <a-input-password v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}"/>
56 56 <#elseif po.classType =='sel_user'>
57   - <j-select-user-by-dep v-model="model.${po.fieldName}"/>
  57 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
58 58 <#elseif po.classType =='textarea'>
59 59 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}"/>
60 60 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
  1 +<#include "/common/utils.ftl">
1 2 package ${bussiPackage}.${entityPackage}.entity;
2 3  
3 4 import java.io.Serializable;
... ... @@ -32,9 +33,9 @@ public class ${entityName} implements Serializable {
32 33 <#-- 生成字典Code -->
33 34 <#assign list_field_dictCode="">
34 35 <#if po.classType='sel_user'>
35   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  36 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
36 37 <#elseif po.classType='sel_depart'>
37   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  38 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
38 39 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
39 40 <#if po.dictTable?default("")?trim?length gt 1>
40 41 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Modal.vuei
... ... @@ -56,9 +56,9 @@
56 56 @input="popupCallback"
57 57 <#if po.readonly=='Y'>disabled</#if>/>
58 58 <#elseif po.classType =='sel_depart'>
59   - <j-select-depart v-model="model.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if>/>
  59 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
60 60 <#elseif po.classType =='sel_user'>
61   - <j-select-user-by-dep v-model="model.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if>/>
  61 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
62 62 <#elseif po.classType =='textarea'>
63 63 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
64 64 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei
... ... @@ -8,6 +8,13 @@
8 8 <#assign hasChildrenField = po.fieldName>
9 9 </#if>
10 10 </#list>
  11 +<#assign list_need_pca=false>
  12 +<#-- 开始循环 -->
  13 +<#list columns as po>
  14 +<#if po.classType=='pca'>
  15 +<#assign list_need_pca=true>
  16 +</#if>
  17 +</#list>
11 18 <template>
12 19 <div>
13 20 <!--引用表格-->
... ... @@ -34,8 +41,20 @@
34 41 </template>
35 42 <!--操作栏-->
36 43 <template #action="{ record }">
37   - <TableAction :actions="getTableAction(record)"/>
  44 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
38 45 </template>
  46 + <!--字段回显插槽-->
  47 + <template #htmlSlot="{text}">
  48 + <div v-html="text"></div>
  49 + </template>
  50 + <!--省市区字段回显插槽-->
  51 + <template #pcaSlot="{text}">
  52 + {{ getAreaTextByCode(text) }}
  53 + </template>
  54 + <template #fileSlot="{text}">
  55 + <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
  56 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  57 + </template>
39 58 </BasicTable>
40 59 <!--字典弹窗-->
41 60 <${entityName}Modal @register="registerModal" @success="handleSuccess"/>
... ... @@ -49,9 +68,12 @@
49 68 import {useModal} from '/@/components/Modal';
50 69 import { useListPage } from '/@/hooks/system/useListPage'
51 70 import ${entityName}Modal from './components/${entityName}Modal.vue';
52   - import {columns} from './${entityName}.data';
  71 + import {columns,searchFormSchema} from './${entityName}.data';
  72 + import { downloadFile } from '/@/utils/common/renderUtils';
53 73 import {list, delete${entityName}, batchDelete${entityName}, getExportUrl,getImportUrl, getChildList,getChildListBatch} from './${entityName}.api';
54   -
  74 + <#if list_need_pca>
  75 + import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
  76 + </#if>
55 77 const expandedRowKeys = ref([]);
56 78 //字典model
57 79 const [registerModal, {openModal}] = useModal();
... ... @@ -62,6 +84,36 @@
62 84 title: '${tableVo.ftlDescription}',
63 85 columns,
64 86 canResize:false,
  87 + formConfig: {
  88 + labelWidth: 120,
  89 + schemas: searchFormSchema,
  90 + autoSubmitOnEnter:true,
  91 + showAdvancedButton:true,
  92 + fieldMapToNumber: [
  93 + <#list columns as po>
  94 + <#if po.isQuery=='Y'>
  95 + <#if po.queryMode!='single'>
  96 + <#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  97 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end']],
  98 + </#if>
  99 + </#if>
  100 + </#if>
  101 + </#list>
  102 + ],
  103 + fieldMapToTime: [
  104 + <#list columns as po>
  105 + <#if po.isQuery=='Y'>
  106 + <#if po.queryMode!='single'>
  107 + <#if po.classType=='date'>
  108 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
  109 + <#elseif po.classType=='datetime'>
  110 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
  111 + </#if>
  112 + </#if>
  113 + </#if>
  114 + </#list>
  115 + ],
  116 + },
65 117 actionColumn: {
66 118 width: 240,
67 119 },
... ... @@ -120,13 +172,13 @@
120 172 */
121 173 async function batchHandleDelete() {
122 174 const ids = selectedRowKeys.value.filter(item => !item.includes('loading'))
123   - await batchDelete${entityName}({ids: ids}, importSuccess);
  175 + await batchDelete${entityName}({id: ids}, importSuccess);
124 176 }
125 177 /**
126 178 * 导入
127 179 */
128 180 function importSuccess() {
129   - reload() && (expandedRowKeys.value = []);
  181 + (selectedRowKeys.value = []) && reload();
130 182 }
131 183 /**
132 184 * 添加下级
... ... @@ -140,12 +192,16 @@
140 192 /**
141 193 * 成功回调
142 194 */
143   - async function handleSuccess({isUpdate, values, expandedArr}) {
  195 + async function handleSuccess({isUpdate, values, expandedArr, changeParent}) {
144 196 if (isUpdate) {
145   - //编辑回调
146   - updateTableDataRecord(values.id, values);
  197 + if (changeParent) {
  198 + reload();
  199 + } else {
  200 + // 编辑回调
  201 + updateTableDataRecord(values.id, values);
  202 + }
147 203 } else {
148   - if(!values['${pidFieldName}']){
  204 + if(!values['id'] || !values['${pidFieldName}']){
149 205 //新增根节点
150 206 reload();
151 207 }else{
... ... @@ -264,18 +320,28 @@
264 320 onClick: handleEdit.bind(null, record),
265 321 },
266 322 {
267   - label: '删除',
268   - popConfirm: {
269   - title: '确定删除吗?',
270   - confirm: handleDelete.bind(null, record),
271   - },
272   - },
273   - {
274 323 label: '添加下级',
275 324 onClick: handleAddSub.bind(null, {${pidFieldName}: record.id}),
276 325 }
277 326 ]
278 327 }
  328 + /**
  329 + * 下拉操作栏
  330 + */
  331 + function getDropDownAction(record){
  332 + return [
  333 + {
  334 + label: '详情',
  335 + onClick: handleDetail.bind(null, record),
  336 + }, {
  337 + label: '删除',
  338 + popConfirm: {
  339 + title: '确定删除吗?',
  340 + confirm: handleDelete.bind(null, record),
  341 + }
  342 + }
  343 + ]
  344 + }
279 345 </script>
280 346  
281 347 <style scoped>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi
... ... @@ -6,7 +6,6 @@ enum Api {
6 6 save='/${entityPackage}/${entityName?uncap_first}/add',
7 7 edit='/${entityPackage}/${entityName?uncap_first}/edit',
8 8 delete${entityName} = '/${entityPackage}/${entityName?uncap_first}/delete',
9   - deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
10 9 importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
11 10 exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
12 11 loadTreeData = '/${entityPackage}/${entityName?uncap_first}/loadTreeRoot',
... ... @@ -49,7 +48,7 @@ export const batchDelete${entityName} = (params, handleSuccess) =&gt; {
49 48 okText: '确认',
50 49 cancelText: '取消',
51 50 onOk: () => {
52   - return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  51 + return defHttp.delete({url: Api.delete${entityName}, data: params}, {joinParamsToUrl: true}).then(() => {
53 52 handleSuccess();
54 53 });
55 54 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi
  1 +<#include "/common/utils.ftl">
1 2 import {BasicColumn} from '/@/components/Table';
2 3 import {FormSchema} from '/@/components/Table';
3 4 import { rules} from '/@/utils/helper/validator';
... ... @@ -28,7 +29,7 @@ export const columns: BasicColumn[] = [
28 29 slots: { customRender: 'htmlSlot' },
29 30 <#elseif po.classType=='pca'>
30 31 dataIndex: '${po.fieldName}',
31   - slots: { customRender: 'pcaSlot' },//TODO 未翻译
  32 + slots: { customRender: 'pcaSlot' },
32 33 <#elseif po.classType=='file'>
33 34 dataIndex: '${po.fieldName}',
34 35 slots: { customRender: 'fileSlot' },
... ... @@ -60,7 +61,7 @@ export const columns: BasicColumn[] = [
60 61 return render.renderCategoryTree(text,'${po.dictField?default("")}')
61 62 },
62 63 <#else>
63   - customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  64 + customRender: ({text, record}) => (text ? record['${po.dictText}'] : '')
64 65 </#if>
65 66 <#else>
66 67 dataIndex: '${po.fieldName}'
... ... @@ -105,9 +106,14 @@ export const searchFormSchema: FormSchema[] = [
105 106 <#elseif po.classType=='sel_depart'>
106 107 component: 'JSelectDept',
107 108 <#elseif po.classType=='list_multi'>
108   - component: 'JMultiSelectTag',//暂无该组件
109   - componentProps:{
110   - dictCode:"query_field_dictCode?default("")"
  109 + component: 'JSelectMultiple',
  110 + componentProps:{
  111 + <#if po.dictTable?default("")?trim?length gt 1>
  112 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}",
  113 + <#elseif po.dictField?default("")?trim?length gt 1>
  114 + dictCode:"${po.dictField}",
  115 + </#if>
  116 + triggerChange: true
111 117 },
112 118 <#elseif po.classType=='cat_tree'>
113 119 component: 'JCategorySelect',
... ... @@ -124,15 +130,22 @@ export const searchFormSchema: FormSchema[] = [
124 130 <#elseif po.classType=='pca'>
125 131 component: 'JAreaLinkage',
126 132 <#elseif po.classType=='popup'>
127   - component: 'JPopup',
128   - componentProps: ({ formActionType }) => {
129   - const {setFieldsValue} = formActionType;
130   - return{
131   - setFieldsValue:setFieldsValue,
132   - code:"${po.dictTable}",
133   - fieldConfig:"${po.dictField}",
134   - multi:${po.extendParams.popupMulti?c},
135   - }
  133 + <#include "/common/form/vue3popup.ftl">
  134 +<#elseif po.classType == 'sel_tree'>
  135 + component: 'JTreeSelect',
  136 + componentProps:{
  137 + <#if po.dictText??>
  138 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  139 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  140 + </#if>
  141 + <#if po.dictText?split(',')[1]??>
  142 + pidField:"${po.dictText?split(',')[1]}",
  143 + </#if>
  144 + <#if po.dictText?split(',')[3]??>
  145 + hasChildField:"${po.dictText?split(',')[3]}",
  146 + </#if>
  147 + </#if>
  148 + pidValue:"${po.dictField}",
136 149 },
137 150 <#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
138 151 <#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
... ... @@ -160,6 +173,13 @@ export const searchFormSchema: FormSchema[] = [
160 173 componentProps: {
161 174 showTime:true
162 175 },
  176 +<#elseif po.classType == 'time'>
  177 + component: 'TimePicker',
  178 + componentProps:{
  179 + valueFormat: 'HH:mm:ss',
  180 + },
  181 +<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  182 + component: 'JRangeNumber',
163 183 <#else>
164 184 component: 'Input', //TODO 范围查询
165 185 </#if>
... ... @@ -192,7 +212,7 @@ export const formSchema: FormSchema[] = [
192 212 </#if>
193 213 {
194 214 label: '${po.filedComment}',
195   - field: '${po.fieldName}',
  215 + field: ${autoStringSuffix(po)},
196 216 <#if po.fieldDbName == tableVo.extendParams.pidField>
197 217 component: 'JTreeSelect',
198 218 componentProps: {
... ... @@ -203,24 +223,19 @@ export const formSchema: FormSchema[] = [
203 223 },
204 224 <#elseif po.classType =='date'>
205 225 component: 'DatePicker',
206   - <#elseif po.fieldType =='datetime'>
  226 + <#elseif po.classType =='datetime'>
207 227 component: 'DatePicker',
208 228 componentProps: {
209   - showTime:true
  229 + showTime:true,
  230 + valueFormat: 'YYYY-MM-DD hh:mm:ss'
210 231 },
211   - <#elseif po.fieldType =='time'>
  232 + <#elseif po.classType =='time'>
212 233 component: 'TimePicker',
  234 + componentProps:{
  235 + valueFormat: 'HH:mm:ss',
  236 + },
213 237 <#elseif po.classType =='popup'>
214   - component: 'JPopup',
215   - componentProps: ({ formActionType }) => {
216   - const {setFieldsValue} = formActionType;
217   - return{
218   - setFieldsValue:setFieldsValue,
219   - code:"${po.dictTable}",
220   - fieldConfig:${po.dictField},
221   - multi:${po.extendParams.popupMulti?c},
222   - }
223   - },
  238 + <#include "/common/form/vue3popup.ftl">
224 239 <#elseif po.classType =='sel_depart'>
225 240 component: 'JSelectDept',
226 241 <#elseif po.classType =='switch'>
... ... @@ -242,14 +257,14 @@ export const formSchema: FormSchema[] = [
242 257 labelKey:'realname',
243 258 },
244 259 <#elseif po.classType =='textarea'>
245   - component: 'InputTextArea',//TODO 注意string转换问题
  260 + component: 'InputTextArea',
246 261 <#elseif po.classType=='list' || po.classType=='radio'>
247 262 component: 'JDictSelectTag',
248 263 componentProps:{
249 264 dictCode:"${form_field_dictCode}"
250 265 },
251 266 <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
252   - component: 'JMultiSelectTag',//TODO 暂无该组件
  267 + component: 'JSelectMultiple',
253 268 componentProps:{
254 269 dictCode:"${form_field_dictCode}"
255 270 },
... ... @@ -284,7 +299,7 @@ export const formSchema: FormSchema[] = [
284 299 </#if>
285 300 },
286 301 <#elseif po.classType=='umeditor'>
287   - component: 'JCodeEditor', //TODO String后缀暂未添加
  302 + component: 'JEditor',
288 303 <#elseif po.classType == 'sel_tree'>
289 304 component: 'JTreeSelect',
290 305 componentProps:{
... ... @@ -318,16 +333,16 @@ export const formSchema: FormSchema[] = [
318 333 </#if>
319 334 <#-- 唯一校验 -->
320 335 <#if fieldValidType == 'only'>
321   - {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  336 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
322 337 <#-- 6到16位数字 -->
323 338 <#elseif fieldValidType == 'n6-16'>
324 339 { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
325 340 <#-- 6到16位任意字符 -->
326 341 <#elseif fieldValidType == '*6-16'>
327 342 { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
328   - <#-- 6到18位字符串 -->
  343 + <#-- 6到18位字 -->
329 344 <#elseif fieldValidType == 's6-18'>
330   - { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  345 + { pattern:/^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
331 346 <#-- 网址 -->
332 347 <#elseif fieldValidType == 'url'>
333 348 { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei
  1 +<#include "/common/utils.ftl">
1 2 <#assign pidFieldName = "">
2 3 <#assign hasChildrenField = "">
3 4 <#list originalColumns as po>
... ... @@ -9,7 +10,7 @@
9 10 </#if>
10 11 </#list>
11 12 <template>
12   - <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
  13 + <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :width="${getModalWidth(tableVo.fieldRowNum?default(1))}" :title="getTitle" @ok="handleSubmit">
13 14 <BasicForm @register="registerForm"/>
14 15 </BasicModal>
15 16 </template>
... ... @@ -24,10 +25,13 @@
24 25 const isUpdate = ref(true);
25 26 const expandedRowKeys = ref([]);
26 27 const treeData = ref([]);
  28 + // 当前编辑的数据
  29 + let model:Nullable<Recordable> = null;
27 30 //表单配置
28   - const [registerForm, {resetFields, setFieldsValue, validate, updateSchema}] = useForm({
  31 + const [registerForm, {setProps,resetFields, setFieldsValue, validate, updateSchema}] = useForm({
29 32 schemas: formSchema,
30 33 showActionButtonGroup: false,
  34 + baseColProps: {span: ${getFormSpan(tableVo.fieldRowNum?default(1))}},
31 35 labelCol: {
32 36 xs: { span: 24 },
33 37 sm: { span: 4 },
... ... @@ -42,16 +46,21 @@
42 46 //重置表单
43 47 await resetFields();
44 48 expandedRowKeys.value = [];
45   - setModalProps({confirmLoading: false, minHeight: 80});
  49 + setModalProps({confirmLoading: false, minHeight: 80 ,showOkBtn: !!!data?.hideFooter});
46 50 isUpdate.value = !!data?.isUpdate;
47 51 if (data?.record) {
  52 + model = data.record;
48 53 //表单赋值
49 54 await setFieldsValue({
50 55 ...data.record,
51 56 });
  57 + } else {
  58 + model = null;
52 59 }
53 60 //父级节点树信息
54 61 treeData.value = await loadTreeData({'async': false,'pcode':''});
  62 + // 隐藏底部时禁用整个表单
  63 + setProps({ disabled: !!data?.hideFooter })
55 64 });
56 65 //设置标题
57 66 const getTitle = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
... ... @@ -85,9 +94,25 @@
85 94 //展开的节点信息
86 95 await getExpandKeysByPid(values['${pidFieldName}'],unref(treeData))
87 96 //刷新列表(isUpdate:是否编辑;values:表单信息;expandedArr:展开的节点信息)
88   - emit('success', {isUpdate: unref(isUpdate), values:{...values},expandedArr: unref(expandedRowKeys).reverse()});
  97 + emit('success', {
  98 + isUpdate: unref(isUpdate),
  99 + values: {...values},
  100 + expandedArr: unref(expandedRowKeys).reverse(),
  101 + // 是否更改了父级节点
  102 + changeParent: model != null && (model['${pidFieldName}'] != values['${pidFieldName}']),
  103 + });
89 104 } finally {
90 105 setModalProps({confirmLoading: false});
91 106 }
92 107 }
93 108 </script>
  109 +<style lang="less" scoped>
  110 + /** 时间和数字输入框样式 */
  111 + :deep(.ant-input-number){
  112 + width: 100%
  113 + }
  114 +
  115 + :deep(.ant-calendar-picker){
  116 + width: 100%
  117 + }
  118 +</style>
94 119 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 +<#assign pidFieldName = "">
  4 +<#assign hasChildrenField = "">
  5 +<#list originalColumns as po>
  6 + <#if po.fieldDbName == tableVo.extendParams.pidField>
  7 + <#assign pidFieldName = po.fieldName>
  8 + </#if>
  9 + <#if po.fieldDbName == tableVo.extendParams.hasChildren>
  10 + <#assign hasChildrenField = po.fieldName>
  11 + </#if>
  12 +</#list>
  13 +<#assign query_field_no=0>
  14 +<#assign need_category = false>
  15 +<#assign need_pca = false>
  16 +<#assign need_search = false>
  17 +<#assign need_dept_user = false>
  18 +<#assign need_switch = false>
  19 +<#assign need_dept = false>
  20 +<#assign need_multi = false>
  21 +<#assign need_popup = false>
  22 +<#assign need_select_tag = false>
  23 +<#assign need_select_tree = false>
  24 +<#assign need_time = false>
  25 +<#assign bpm_flag=false>
  26 +<#assign need_markdown = false>
  27 +<#assign need_upload = false>
  28 +<#assign need_image_upload = false>
  29 +<#assign need_editor = false>
  30 +<#assign need_checkbox = false>
  31 +<#assign query_flag = false>
  32 + <!--查询区域-->
  33 + <div class="jeecg-basic-table-form-container">
  34 + <a-form @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
  35 + <a-row :gutter="24">
  36 +<#-- 开始循环 -->
  37 +<#list columns as po>
  38 +<#if po.fieldDbName=='bpm_status'>
  39 + <#assign bpm_flag=true>
  40 +</#if>
  41 +<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
  42 +<#assign need_category=true>
  43 +</#if>
  44 +<#if po.classType=='pca'>
  45 +<#assign need_pca=true>
  46 +</#if>
  47 +<#if po.classType=='sel_search'>
  48 +<#assign need_search = true>
  49 +</#if>
  50 +<#if po.classType=='sel_user'>
  51 +<#assign need_dept_user = true>
  52 +</#if>
  53 +<#if po.classType=='sel_depart'>
  54 +<#assign need_dept = true>
  55 +</#if>
  56 +<#if po.classType=='switch'>
  57 +<#assign need_switch = true>
  58 +</#if>
  59 +<#if po.classType=='list_multi'>
  60 +<#assign need_multi = true>
  61 +</#if>
  62 +<#if po.classType=='popup'>
  63 +<#assign need_popup = true>
  64 +</#if>
  65 +<#if po.classType=='sel_tree'>
  66 +<#assign need_select_tree = true>
  67 +</#if>
  68 +<#if po.classType=='time'>
  69 +<#assign need_time = true>
  70 +</#if>
  71 + <#include "/common/form/native/vue3NativeSearch.ftl">
  72 +</#list>
  73 +<#if query_field_no gt 2>
  74 + </template>
  75 +</#if>
  76 +<#if query_flag>
  77 + <a-col :xl="6" :lg="7" :md="8" :sm="24">
  78 + <span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
  79 + <a-col :lg="6">
  80 + <a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery">查询</a-button>
  81 + <a-button type="primary" preIcon="ant-design:reload-outlined" @click="searchReset" style="margin-left: 8px">重置</a-button>
  82 + <a @click="toggleSearchStatus = !toggleSearchStatus" style="margin-left: 8px">
  83 + {{ toggleSearchStatus ? '收起' : '展开' }}
  84 + <Icon :icon="toggleSearchStatus ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
  85 + </a>
  86 + </a-col>
  87 + </span>
  88 + </a-col>
  89 +</#if>
  90 + </a-row>
  91 + </a-form>
  92 + </div>
  93 +<#-- 结束循环 -->
  94 + <!--引用表格-->
  95 + <BasicTable @register="registerTable" :rowSelection="rowSelection" :expandedRowKeys="expandedRowKeys" @expand="handleExpand" @fetch-success="onFetchSuccess">
  96 + <!--插槽:table标题-->
  97 + <template #tableTitle>
  98 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  99 + <a-dropdown v-if="selectedRowKeys.length > 0">
  100 + <template #overlay>
  101 + <a-menu>
  102 + <a-menu-item key="1" @click="batchHandleDelete">
  103 + <Icon icon="ant-design:delete-outlined"></Icon>
  104 + 删除
  105 + </a-menu-item>
  106 + </a-menu>
  107 + </template>
  108 + <a-button
  109 + >批量操作
  110 + <Icon icon="ant-design:down-outlined"></Icon>
  111 + </a-button>
  112 + </a-dropdown>
  113 + </template>
  114 + <!--操作栏-->
  115 + <template #action="{ record }">
  116 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
  117 + </template>
  118 + <!--字段回显插槽-->
  119 + <template #htmlSlot="{ text }">
  120 + <div v-html="text"></div>
  121 + </template>
  122 + <!--省市区字段回显插槽-->
  123 + <template #pcaSlot="{ text }">
  124 + {{ getAreaTextByCode(text) }}
  125 + </template>
  126 + <template #fileSlot="{ text }">
  127 + <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
  128 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  129 + </template>
  130 + </BasicTable>
  131 + <!-- 表单区域 -->
  132 + <${entityName}Modal ref="registerModal" @success="handleSuccess"></${entityName}Modal>
  133 + </div>
  134 +</template>
  135 +
  136 +<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
  137 + import { ref, reactive, unref } from 'vue';
  138 + import { BasicTable, useTable, TableAction } from '/@/components/Table';
  139 + import { useListPage } from '/@/hooks/system/useListPage';
  140 + import { columns } from './${entityName}.data';
  141 + import {list, delete${entityName}, batchDelete${entityName}, getExportUrl,getImportUrl, getChildList,getChildListBatch} from './${entityName}.api';
  142 + import { downloadFile } from '/@/utils/common/renderUtils';
  143 + import ${entityName}Modal from './components/${entityName}Modal.vue'
  144 + <#include "/common/form/native/vue3NativeImport.ftl">
  145 +<#if need_category>
  146 + import { loadCategoryData } from '/@/api/common/api';
  147 + import { getAuthCache, setAuthCache } from '/@/utils/auth';
  148 + import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
  149 +</#if>
  150 +<#if need_pca>
  151 + import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
  152 +</#if>
  153 +
  154 + const expandedRowKeys = ref([]);
  155 + const queryParam = ref<any>({});
  156 + const toggleSearchStatus = ref<boolean>(false);
  157 + const registerModal = ref();
  158 + //注册table数据
  159 + const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
  160 + tableProps: {
  161 + title: '${tableVo.ftlDescription}',
  162 + api: list,
  163 + columns,
  164 + canResize:false,
  165 + useSearchForm: false,
  166 + actionColumn: {
  167 + width: 120,
  168 + fixed: 'right',
  169 + },
  170 + beforeFetch: (params) => {
  171 + return Object.assign(params, queryParam.value);
  172 + },
  173 + },
  174 + exportConfig: {
  175 + name: "${tableVo.ftlDescription}",
  176 + url: getExportUrl,
  177 + },
  178 + importConfig: {
  179 + url: getImportUrl,
  180 + success: success
  181 + },
  182 + });
  183 + const [registerTable, {reload, collapseAll, updateTableDataRecord, findTableDataRecord,getDataSource},{ rowSelection, selectedRowKeys }] = tableContext
  184 + const labelCol = reactive({
  185 + xs: { span: 24 },
  186 + sm: { span: 7 },
  187 + });
  188 + const wrapperCol = reactive({
  189 + xs: { span: 24 },
  190 + sm: { span: 16 },
  191 + });
  192 +
  193 + /**
  194 + * 新增事件
  195 + */
  196 + function handleAdd() {
  197 + registerModal.value.disableSubmit = false;
  198 + registerModal.value.add();
  199 + }
  200 +
  201 + /**
  202 + * 编辑事件
  203 + */
  204 + async function handleEdit(record) {
  205 + registerModal.value.disableSubmit = false;
  206 + registerModal.value.edit(record);
  207 + }
  208 +
  209 + /**
  210 + * 详情
  211 + */
  212 + async function handleDetail(record) {
  213 + registerModal.value.disableSubmit = true;
  214 + registerModal.value.edit(record);
  215 + }
  216 +
  217 + /**
  218 + * 删除事件
  219 + */
  220 + async function handleDelete(record) {
  221 + await delete${entityName}({ id: record.id }, success);
  222 + }
  223 +
  224 + /**
  225 + * 批量删除事件
  226 + */
  227 + async function batchHandleDelete() {
  228 + const ids = selectedRowKeys.value.filter((item) => !item.includes('loading'));
  229 + await batchDelete${entityName}({ id: ids }, success);
  230 + }
  231 +
  232 + /**
  233 + * 成功回调刷新页面
  234 + */
  235 + function success() {
  236 + (selectedRowKeys.value = []) && reload();
  237 + }
  238 +
  239 + /**
  240 + * 添加下级
  241 + */
  242 + function handleAddSub(record) {
  243 + registerModal.value.disableSubmit = false;
  244 + registerModal.value.add(record);
  245 + }
  246 +
  247 + /**
  248 + * 成功回调
  249 + */
  250 + async function handleSuccess({ isUpdate, values, expandedArr, changeParent }) {
  251 + if (isUpdate) {
  252 + if (changeParent) {
  253 + reload();
  254 + } else {
  255 + // 编辑回调
  256 + updateTableDataRecord(values.id, values);
  257 + }
  258 + } else {
  259 + if (!values['id'] || !values['pid']) {
  260 + //新增根节点
  261 + reload();
  262 + } else {
  263 + //新增子集
  264 + expandedRowKeys.value = [];
  265 + for (let key of unref(expandedArr)) {
  266 + await expandTreeNode(key);
  267 + }
  268 + }
  269 + }
  270 + }
  271 +
  272 + /**
  273 + * 接口请求成功后回调
  274 + */
  275 + function onFetchSuccess(result) {
  276 + getDataByResult(result.items) && loadDataByExpandedRows();
  277 + }
  278 +
  279 + /**
  280 + * 根据已展开的行查询数据(用于保存后刷新时异步加载子级的数据)
  281 + */
  282 + async function loadDataByExpandedRows() {
  283 + if (unref(expandedRowKeys).length > 0) {
  284 + const res = await getChildListBatch({ parentIds: unref(expandedRowKeys).join(',') });
  285 + if (res.success && res.result.records.length > 0) {
  286 + //已展开的数据批量子节点
  287 + let records = res.result.records;
  288 + const listMap = new Map();
  289 + for (let item of records) {
  290 + let pid = item['${pidFieldName}'];
  291 + if (unref(expandedRowKeys).includes(pid)) {
  292 + let mapList = listMap.get(pid);
  293 + if (mapList == null) {
  294 + mapList = [];
  295 + }
  296 + mapList.push(item);
  297 + listMap.set(pid, mapList);
  298 + }
  299 + }
  300 + let childrenMap = listMap;
  301 + let fn = (list) => {
  302 + if (list) {
  303 + list.forEach((data) => {
  304 + if (unref(expandedRowKeys).includes(data.id)) {
  305 + data.children = getDataByResult(childrenMap.get(data.id));
  306 + fn(data.children);
  307 + }
  308 + });
  309 + }
  310 + };
  311 + fn(getDataSource());
  312 + }
  313 + }
  314 + }
  315 +
  316 + /**
  317 + * 处理数据集
  318 + */
  319 + function getDataByResult(result) {
  320 + if (result && result.length > 0) {
  321 + return result.map((item) => {
  322 + //判断是否标记了带有子节点
  323 + if (item['hasChild'] == '1') {
  324 + let loadChild = { id: item.id + '_loadChild', name: 'loading...', isLoading: true };
  325 + item.children = [loadChild];
  326 + }
  327 + return item;
  328 + });
  329 + }
  330 + }
  331 +
  332 + /**
  333 + *树节点展开合并
  334 + */
  335 + async function handleExpand(expanded, record) {
  336 + // 判断是否是展开状态,展开状态(expanded)并且存在子集(children)并且未加载过(isLoading)的就去查询子节点数据
  337 + if (expanded) {
  338 + expandedRowKeys.value.push(record.id);
  339 + if (record.children.length > 0 && !!record.children[0].isLoading) {
  340 + let result = await getChildList({ ${pidFieldName}: record.id});
  341 + result = result.records ? result.records : result;
  342 + if (result && result.length > 0) {
  343 + record.children = getDataByResult(result);
  344 + } else {
  345 + record.children = null;
  346 + record.hasChild = '0';
  347 + }
  348 + }
  349 + } else {
  350 + let keyIndex = expandedRowKeys.value.indexOf(record.id);
  351 + if (keyIndex >= 0) {
  352 + expandedRowKeys.value.splice(keyIndex, 1);
  353 + }
  354 + }
  355 + }
  356 +
  357 + /**
  358 + * 操作表格后处理树节点展开合并
  359 + */
  360 + async function expandTreeNode(key) {
  361 + let record = findTableDataRecord(key);
  362 + expandedRowKeys.value.push(key);
  363 + let result = await getChildList({ ${pidFieldName}: key });
  364 + if (result && result.length > 0) {
  365 + record.children = getDataByResult(result);
  366 + } else {
  367 + record.children = null;
  368 + record.hasChild = '0';
  369 + }
  370 + updateTableDataRecord(key, record);
  371 + }
  372 +
  373 + /**
  374 + * 操作栏
  375 + */
  376 + function getTableAction(record) {
  377 + return [
  378 + {
  379 + label: '编辑',
  380 + onClick: handleEdit.bind(null, record),
  381 + }
  382 + ];
  383 + }
  384 +
  385 + /**
  386 + * 下拉操作栏
  387 + */
  388 + function getDropDownAction(record) {
  389 + return [
  390 + {
  391 + label: '详情',
  392 + onClick: handleDetail.bind(null, record),
  393 + },
  394 + {
  395 + label: '添加下级',
  396 + onClick: handleAddSub.bind(null, { pid: record.id }),
  397 + },
  398 + {
  399 + label: '删除',
  400 + popConfirm: {
  401 + title: '确定删除吗?',
  402 + confirm: handleDelete.bind(null, record),
  403 + },
  404 + },
  405 + ];
  406 + }
  407 +
  408 + /**
  409 + * 查询
  410 + */
  411 + function searchQuery() {
  412 + reload();
  413 + }
  414 +
  415 + /**
  416 + * 重置
  417 + */
  418 + function searchReset() {
  419 + queryParam.value = {};
  420 + selectedRowKeys.value = [];
  421 + //刷新数据
  422 + reload();
  423 + }
  424 +
  425 + <#if need_popup>
  426 + /**
  427 + * popup组件值改变事件
  428 + */
  429 + function setFieldsValue(map) {
  430 + Object.keys(map).map((key) => {
  431 + queryParam.value[key] = map[key];
  432 + });
  433 + }
  434 + </#if>
  435 +
  436 + <#if need_pca>
  437 + /**
  438 + * 省市区点击事件
  439 + * @param key
  440 + * @param value
  441 + */
  442 + function handleAreaChange(key, value) {
  443 + queryParam.value[key] = value.join(',');
  444 + }
  445 + </#if>
  446 +
  447 + <#if need_category>
  448 + /**
  449 + * form点击事件
  450 + * @param value
  451 + */
  452 + function handleFormChange(key, value) {
  453 + queryParam.value[key] = value;
  454 + }
  455 +
  456 + /**
  457 + * 初始化字典配置
  458 + */
  459 + function initDictConfig() {
  460 + <#list columns as po>
  461 + <#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
  462 + <#if po.classType=='cat_tree' && need_category==true>
  463 + loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
  464 + if (res) {
  465 + let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
  466 + if(!allDictDate['${po.dictField?default("")}']){
  467 + Object.assign(allDictDate,{'${po.dictField?default("")}':res})
  468 + }
  469 + setAuthCache(DB_DICT_DATA_KEY,allDictDate)
  470 + }
  471 + });
  472 + </#if>
  473 + </#if>
  474 + </#list>
  475 + }
  476 + initDictConfig();
  477 + </#if>
  478 +</script>
  479 +
  480 +<style lang="less" scoped>
  481 + .jeecg-basic-table-form-container {
  482 + .table-page-search-submitButtons {
  483 + display: block;
  484 + margin-bottom: 24px;
  485 + white-space: nowrap;
  486 + }
  487 + .query-group-cust{
  488 + width: calc(50% - 15px);
  489 + min-width: 100px !important;
  490 + }
  491 + .query-group-split-cust{
  492 + width: 30px;
  493 + display: inline-block;
  494 + text-align: center
  495 + }
  496 + }
  497 +</style>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}__api.tsi 0 → 100644
  1 +import { defHttp } from "/@/utils/http/axios";
  2 +import { Modal } from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/rootList',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + delete${entityName} = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  10 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  11 + loadTreeData = '/${entityPackage}/${entityName?uncap_first}/loadTreeRoot',
  12 + getChildList = '/${entityPackage}/${entityName?uncap_first}/childList',
  13 + getChildListBatch = '/${entityPackage}/${entityName?uncap_first}/getChildListBatch',
  14 +}
  15 +
  16 +/**
  17 + * 导出api
  18 + * @param params
  19 + */
  20 +export const getExportUrl = Api.exportXls;
  21 +
  22 +/**
  23 + * 导入api
  24 + * @param params
  25 + */
  26 +export const getImportUrl = Api.importExcel;
  27 +
  28 +/**
  29 + * 列表接口
  30 + * @param params
  31 + */
  32 +export const list = (params) => defHttp.get({ url: Api.list, params });
  33 +
  34 +/**
  35 + * 删除
  36 + * @param params
  37 + * @param handleSuccess
  38 + */
  39 +export const delete${entityName} = (params,handleSuccess) => {
  40 + return defHttp.delete({ url: Api.delete${entityName}, params }, { joinParamsToUrl: true }).then(() => {
  41 + handleSuccess();
  42 + });
  43 +}
  44 +
  45 +/**
  46 + * 批量删除
  47 + * @param params
  48 + * @param handleSuccess
  49 + */
  50 +export const batchDelete${entityName} = (params, handleSuccess) => {
  51 + Modal.confirm({
  52 + title: '确认删除',
  53 + content: '是否删除选中数据',
  54 + okText: '确认',
  55 + cancelText: '取消',
  56 + onOk: () => {
  57 + return defHttp.delete({ url: Api.delete${entityName}, data: params }, { joinParamsToUrl: true }).then(() => {
  58 + handleSuccess();
  59 + });
  60 + }
  61 + });
  62 +}
  63 +
  64 +/**
  65 + * 保存或者更新
  66 + * @param params
  67 + * @param isUpdate
  68 + */
  69 +export const saveOrUpdateDict = (params, isUpdate) => {
  70 + let url = isUpdate ? Api.edit : Api.save;
  71 + return defHttp.post({ url: url, params },{ isTransformResponse:false });
  72 +}
  73 +
  74 +/**
  75 + * 查询全部树形节点数据
  76 + * @param params
  77 + */
  78 +export const loadTreeData = (params) => defHttp.get({ url: Api.loadTreeData,params });
  79 +
  80 +/**
  81 + * 查询子节点数据
  82 + * @param params
  83 + */
  84 +export const getChildList = (params) => defHttp.get({ url: Api.getChildList, params });
  85 +
  86 +/**
  87 + * 批量查询子节点数据
  88 + * @param params
  89 + */
  90 +export const getChildListBatch = (params) => defHttp.get({ url: Api.getChildListBatch, params },{ isTransformResponse:false });
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}__data.tsi 0 → 100644
  1 +<#include "/common/utils.ftl">
  2 +import {BasicColumn} from '/@/components/Table';
  3 +import {FormSchema} from '/@/components/Table';
  4 +import { rules} from '/@/utils/helper/validator';
  5 +import { render } from '/@/utils/common/renderUtils';
  6 +//列表数据
  7 +export const columns: BasicColumn[] = [
  8 + <#list columns as po>
  9 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  10 + {
  11 + title: '${po.filedComment}',
  12 + <#if po.fieldDbName == tableVo.extendParams.textField>
  13 + align: 'left',
  14 + <#else>
  15 + align: 'center',
  16 + </#if>
  17 + <#if po.sort=='Y'>
  18 + sorter: true,
  19 + </#if>
  20 + <#if po.classType=='date'>
  21 + dataIndex: '${po.fieldName}',
  22 + customRender:({text}) =>{
  23 + return !text?"":(text.length>10?text.substr(0,10):text);
  24 + },
  25 + <#elseif po.fieldDbType=='Blob'>
  26 + dataIndex: '${po.fieldName}String'
  27 + <#elseif po.classType=='umeditor'>
  28 + dataIndex: '${po.fieldName}',
  29 + slots: { customRender: 'htmlSlot' },
  30 + <#elseif po.classType=='pca'>
  31 + dataIndex: '${po.fieldName}',
  32 + slots: { customRender: 'pcaSlot' },
  33 + <#elseif po.classType=='file'>
  34 + dataIndex: '${po.fieldName}',
  35 + slots: { customRender: 'fileSlot' },
  36 + <#elseif po.classType=='image'>
  37 + dataIndex: '${po.fieldName}',
  38 + customRender: render.renderImage,
  39 + <#elseif po.classType=='switch'>
  40 + dataIndex: '${po.fieldName}',
  41 +<#assign switch_extend_arr=['Y','N']>
  42 +<#if po.dictField?default("")?contains("[")>
  43 +<#assign switch_extend_arr=po.dictField?eval>
  44 +</#if>
  45 +<#list switch_extend_arr as a>
  46 +<#if a_index == 0>
  47 +<#assign switch_extend_arr1=a>
  48 +<#else>
  49 +<#assign switch_extend_arr2=a>
  50 +</#if>
  51 +</#list>
  52 + customRender:({text}) => {
  53 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}]);
  54 + },
  55 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  56 + dataIndex: '${po.fieldName}_dictText'
  57 + <#elseif po.classType=='cat_tree'>
  58 + dataIndex: '${po.fieldName}',
  59 + <#if po.dictText?default("")?trim?length == 0>
  60 + customRender:({text}) => {
  61 + return render.renderCategoryTree(text,'${po.dictField?default("")}');
  62 + },
  63 + <#else>
  64 + customRender: ({text, record}) => (text ? record['${po.dictText}'] : '');
  65 + </#if>
  66 + <#else>
  67 + dataIndex: '${po.fieldName}'
  68 + </#if>
  69 + },
  70 + </#if>
  71 + </#list>
  72 +];
  73 +
  74 +//查询数据
  75 +export const searchFormSchema: FormSchema[] = [
  76 +<#-- 开始循环 -->
  77 +<#list columns as po>
  78 +<#if po.fieldDbName=='bpm_status'>
  79 + <#assign bpm_flag=true>
  80 +</#if>
  81 +<#if po.isQuery=='Y'>
  82 +<#assign query_flag=true>
  83 + <#assign query_field_dictCode="">
  84 + <#if po.dictTable?default("")?trim?length gt 1>
  85 + <#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  86 + <#elseif po.dictField?default("")?trim?length gt 1>
  87 + <#assign query_field_dictCode="${po.dictField}">
  88 + </#if>
  89 +<#if po.queryMode=='single'>
  90 + {
  91 + label: "${po.filedComment}",
  92 + field: "${po.fieldName}",
  93 +<#if po.classType=='sel_search'>
  94 + component: 'JSearchSelect',
  95 + componentProps: {
  96 + dict:"${po.dictTable},${po.dictText},${po.dictField}"
  97 + },
  98 +<#elseif po.classType=='sel_user'>
  99 + component: 'JSelectUserByDept',
  100 +<#elseif po.classType=='switch'>
  101 + component: 'JSwitch',
  102 + componentProps: {
  103 + <#if po.dictField != 'is_open'>
  104 + options: '${po.dictField}',
  105 + </#if>
  106 + },
  107 + <#elseif po.classType=='sel_depart'>
  108 + component: 'JSelectDept',
  109 + <#elseif po.classType=='list_multi'>
  110 + component: 'JSelectMultiple',
  111 + componentProps: {
  112 + <#if po.dictTable?default("")?trim?length gt 1>
  113 + options: "${po.dictField}",
  114 + dictCode: "${po.dictTable},${po.dictText},${po.dictField}",
  115 + <#elseif po.dictField?default("")?trim?length gt 1>
  116 + dictCode: "${po.dictField}",
  117 + </#if>
  118 + triggerChange: true
  119 + },
  120 + <#elseif po.classType=='cat_tree'>
  121 + component: 'JCategorySelect',
  122 + componentProps:{
  123 + pcode:"${po.dictField?default("")}",//back和事件未添加,暂时有问题
  124 + },
  125 +<#elseif po.classType=='date'>
  126 + component: 'DatePicker',
  127 +<#elseif po.classType=='datetime'>
  128 + component: 'DatePicker',
  129 + componentProps: {
  130 + showTime: true
  131 + },
  132 +<#elseif po.classType=='pca'>
  133 + component: 'JAreaLinkage',
  134 +<#elseif po.classType=='popup'>
  135 + <#include "/common/form/vue3popup.ftl">
  136 +<#elseif po.classType == 'sel_tree'>
  137 + component: 'JTreeSelect',
  138 + componentProps: {
  139 + <#if po.dictText??>
  140 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  141 + dict: "${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  142 + </#if>
  143 + <#if po.dictText?split(',')[1]??>
  144 + pidField: "${po.dictText?split(',')[1]}",
  145 + </#if>
  146 + <#if po.dictText?split(',')[3]??>
  147 + hasChildField: "${po.dictText?split(',')[3]}",
  148 + </#if>
  149 + </#if>
  150 + pidValue: "${po.dictField}",
  151 + },
  152 +<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  153 +<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
  154 + component: 'JDictSelectTag',
  155 + componentProps:{
  156 + <#if po.dictTable?default("")?trim?length gt 1>
  157 + dictCode: "${po.dictTable},${po.dictText},${po.dictField}"
  158 + <#elseif po.dictField?default("")?trim?length gt 1>
  159 + dictCode: "${po.dictField}"
  160 + </#if>
  161 + },
  162 +<#else>
  163 + component: 'Input',
  164 +</#if>
  165 + colProps: {span: 6},
  166 + },
  167 +<#else>
  168 + {
  169 + label: "${po.filedComment}",
  170 + field: "${po.fieldName}",
  171 +<#if po.classType=='date'>
  172 + component: 'RangePicker',
  173 +<#elseif po.classType=='datetime'>
  174 + component: 'RangePicker',
  175 + componentProps: {
  176 + showTime: true
  177 + },
  178 +<#elseif po.classType == 'time'>
  179 + component: 'TimePicker',
  180 + componentProps:{
  181 + valueFormat: 'HH:mm:ss',
  182 + },
  183 +<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  184 + component: 'JRangeNumber',
  185 +<#else>
  186 + component: 'Input', //TODO 范围查询
  187 +</#if>
  188 + colProps: {span: 6},
  189 + },
  190 +</#if>
  191 +</#if>
  192 +</#list>
  193 +<#-- 结束循环 -->
  194 +];
  195 +
  196 +//表单数据
  197 +export const formSchema: FormSchema[] = [
  198 +<#assign form_cat_tree = false>
  199 +<#assign form_cat_back = "">
  200 +<#assign bpm_flag=false>
  201 +<#assign id_exists = false>
  202 +<#list columns as po><#rt/>
  203 +<#if po.fieldDbName=='bpm_status'>
  204 + <#assign bpm_flag=true>
  205 +</#if>
  206 +<#if po.fieldDbName == 'id'>
  207 + <#assign id_exists = true>
  208 +</#if>
  209 +<#if po.isShow =='Y'>
  210 +<#assign form_field_dictCode="">
  211 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  212 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  213 + <#elseif po.dictField?default("")?trim?length gt 1>
  214 + <#assign form_field_dictCode="${po.dictField}">
  215 + </#if>
  216 + {
  217 + label: '${po.filedComment}',
  218 + field: ${autoStringSuffix(po)},
  219 + <#if po.fieldDbName == tableVo.extendParams.pidField>
  220 + component: 'JTreeSelect',
  221 + componentProps: {
  222 + dict: "${tableVo.tableName},${tableVo.extendParams.textField},id",
  223 + pidField: "${tableVo.extendParams.pidField}",
  224 + pidValue: "0",
  225 + hasChildField: "${tableVo.extendParams.hasChildren}",
  226 + },
  227 + <#elseif po.classType =='date'>
  228 + component: 'DatePicker',
  229 + <#elseif po.classType =='datetime'>
  230 + component: 'DatePicker',
  231 + componentProps: {
  232 + showTime:true,
  233 + valueFormat: 'YYYY-MM-DD hh:mm:ss'
  234 + },
  235 + <#elseif po.classType =='time'>
  236 + component: 'TimePicker',
  237 + componentProps:{
  238 + valueFormat: 'HH:mm:ss',
  239 + },
  240 + <#elseif po.classType =='popup'>
  241 + <#include "/common/form/vue3popup.ftl">
  242 + <#elseif po.classType =='sel_depart'>
  243 + component: 'JSelectDept',
  244 + <#elseif po.classType =='switch'>
  245 + component: 'JSwitch',
  246 + componentProps:{
  247 + <#if po.dictField != 'is_open'>
  248 + options:${po.dictField}
  249 + </#if>
  250 + },
  251 + <#elseif po.classType =='pca'>
  252 + component: 'JAreaLinkage',
  253 + <#elseif po.classType =='markdown'>
  254 + component: 'JMarkdownEditor',//注意string转换问题
  255 + <#elseif po.classType =='password'>
  256 + component: 'InputPassword',
  257 + <#elseif po.classType =='sel_user'>
  258 + component: 'JSelectUserByDept',
  259 + componentProps:{
  260 + labelKey: 'realname',
  261 + },
  262 + <#elseif po.classType =='textarea'>
  263 + component: 'InputTextArea',
  264 + <#elseif po.classType=='list' || po.classType=='radio'>
  265 + component: 'JDictSelectTag',
  266 + componentProps:{
  267 + dictCode: "${form_field_dictCode}"
  268 + },
  269 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  270 + component: 'JSelectMultiple',
  271 + componentProps:{
  272 + dictCode: "${form_field_dictCode}"
  273 + },
  274 + <#elseif po.classType=='sel_search'>
  275 + component: 'JSearchSelect',
  276 + componentProps:{
  277 + dict: "${form_field_dictCode}"
  278 + },
  279 +<#elseif po.classType=='cat_tree'>
  280 + <#assign form_cat_tree = true>
  281 + component: 'JCategorySelect',
  282 + componentProps:{
  283 + pcode: "${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  284 + },
  285 + <#if po.dictText?default("")?trim?length gt 1>
  286 + <#assign form_cat_back = "${po.dictText}">
  287 + </#if>
  288 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  289 + component: 'InputNumber',
  290 + <#elseif po.classType=='file'>
  291 + component: 'JUpload',
  292 + componentProps:{
  293 + <#if po.uploadnum??>
  294 + maxCount: ${po.uploadnum}
  295 + </#if>
  296 + },
  297 + <#elseif po.classType=='image'>
  298 + component: 'JImageUpload',
  299 + componentProps:{
  300 + <#if po.uploadnum??>
  301 + fileMax: ${po.uploadnum}
  302 + </#if>
  303 + },
  304 + <#elseif po.classType=='umeditor'>
  305 + component: 'JEditor',
  306 + <#elseif po.classType == 'sel_tree'>
  307 + component: 'JTreeSelect',
  308 + componentProps:{
  309 + <#if po.dictText??>
  310 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  311 + dict: "${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  312 + </#if>
  313 + <#if po.dictText?split(',')[1]??>
  314 + pidField: "${po.dictText?split(',')[1]}",
  315 + </#if>
  316 + <#if po.dictText?split(',')[3]??>
  317 + hasChildField: "${po.dictText?split(',')[3]}",
  318 + </#if>
  319 + </#if>
  320 + pidValue: "${po.dictField}",
  321 + },
  322 + <#else>
  323 + component: 'Input',
  324 + </#if>
  325 + <#include "/common/utils.ftl">
  326 + <#if po.isShow == 'Y' && poHasCheck(po)>
  327 + dynamicRules: ({ model, schema }) => {
  328 + <#if po.fieldName != 'id'>
  329 + <#assign fieldValidType = po.fieldValidType!''>
  330 + return [
  331 + <#-- 非空校验 -->
  332 + <#if po.nullable == 'N' || fieldValidType == '*'>
  333 + { required: true, message: '请输入${po.filedComment}!' },
  334 + <#elseif fieldValidType!=''>
  335 + { required: false },
  336 + </#if>
  337 + <#-- 唯一校验 -->
  338 + <#if fieldValidType == 'only'>
  339 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
  340 + <#-- 6到16位数字 -->
  341 + <#elseif fieldValidType == 'n6-16'>
  342 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!' },
  343 + <#-- 6到16位任意字符 -->
  344 + <#elseif fieldValidType == '*6-16'>
  345 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!' },
  346 + <#-- 6到18位字母 -->
  347 + <#elseif fieldValidType == 's6-18'>
  348 + { pattern:/^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!' },
  349 + <#-- 网址 -->
  350 + <#elseif fieldValidType == 'url'>
  351 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!' },
  352 + <#-- 电子邮件 -->
  353 + <#elseif fieldValidType == 'e'>
  354 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件! },
  355 + <#-- 手机号码 -->
  356 + <#elseif fieldValidType == 'm'>
  357 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!' },
  358 + <#-- 邮政编码 -->
  359 + <#elseif fieldValidType == 'p'>
  360 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!' },
  361 + <#-- 字母 -->
  362 + <#elseif fieldValidType == 's'>
  363 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!' },
  364 + <#-- 数字 -->
  365 + <#elseif fieldValidType == 'n'>
  366 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!' },
  367 + <#-- 整数 -->
  368 + <#elseif fieldValidType == 'z'>
  369 + { pattern: /^-?\d+$/, message: '请输入整数!' },
  370 + <#-- 金额 -->
  371 + <#elseif fieldValidType == 'money'>
  372 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!' },
  373 + <#-- 正则校验 -->
  374 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  375 + { pattern: '${fieldValidType}', message: '不符合校验规则!' },
  376 + <#-- 无校验 -->
  377 + <#else>
  378 + <#t>
  379 + </#if>
  380 + ];
  381 + </#if>
  382 + },
  383 + </#if>
  384 + <#if po.readonly=='Y'>
  385 + dynamicDisabled: true
  386 + </#if>
  387 + },
  388 +</#if>
  389 +</#list>
  390 +<#if id_exists == false>
  391 + // TODO 主键隐藏字段,目前写死为ID
  392 + {
  393 + label: '',
  394 + field: 'id',
  395 + component: 'Input',
  396 + show: false,
  397 + },
  398 +</#if>
  399 +];
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3Native/components/${entityName}Form.vuei 0 → 100644
  1 +<#include "/common/utils.ftl">
  2 +<template>
  3 + <a-spin :spinning="confirmLoading">
  4 + <a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
  5 + <a-row>
  6 +<#assign need_category = false>
  7 +<#assign bpm_flag=false>
  8 +<#assign need_pca = false>
  9 +<#assign need_search = false>
  10 +<#assign need_dept_user = false>
  11 +<#assign need_switch = false>
  12 +<#assign need_dept = false>
  13 +<#assign need_multi = false>
  14 +<#assign need_popup = false>
  15 +<#assign need_select_tag = false>
  16 +<#assign need_select_tree = false>
  17 +<#assign need_time = false>
  18 +<#assign need_markdown = false>
  19 +<#assign need_upload = false>
  20 +<#assign need_image_upload = false>
  21 +<#assign need_editor = false>
  22 +<#assign need_checkbox = false>
  23 +<#assign pidFieldName = "">
  24 +<#assign hasOnlyValidate = false>
  25 +<#assign form_span = 24>
  26 +<#if tableVo.fieldRowNum==2>
  27 +<#assign form_span = 12>
  28 +<#elseif tableVo.fieldRowNum==3>
  29 +<#assign form_span = 8>
  30 +<#elseif tableVo.fieldRowNum==4>
  31 +<#assign form_span = 6>
  32 +</#if>
  33 +<#list columns as po>
  34 +<#if po.fieldDbName=='bpm_status'>
  35 + <#assign bpm_flag=true>
  36 +</#if>
  37 +<#if po.isShow == 'Y' && po.fieldValidType?default("") == 'only'>
  38 + <#assign hasOnlyValidate = true>
  39 +</#if>
  40 + <#if po.fieldDbName == tableVo.extendParams.pidField>
  41 + <#assign pidFieldName = po.fieldName>
  42 + <a-col :span="${form_span}">
  43 + <a-form-item label="父级节点" v-bind="validateInfos.${autoStringSuffixForModel(po)}">
  44 + <j-tree-select
  45 + placeholder="请选择${po.filedComment}"
  46 + v-model:value="formData.${po.fieldName}"
  47 + dict="${tableVo.tableName},${tableVo.extendParams.textField},id"
  48 + pidField="${tableVo.extendParams.pidField}"
  49 + pidValue="0"
  50 + hasChildField="${tableVo.extendParams.hasChildren}"
  51 + <#if po.readonly=='Y'>disabled<#else>:disabled="disabled"</#if>>
  52 + </j-tree-select>
  53 + </a-form-item>
  54 + </a-col>
  55 + </#if>
  56 + <#include "/common/form/native/vue3NativeForm.ftl">
  57 +</#list>
  58 + </a-row>
  59 + </a-form>
  60 + </a-spin>
  61 +</template>
  62 +
  63 +<script lang="ts" setup>
  64 + import { ref, reactive, defineExpose, nextTick, unref, defineProps, computed } from 'vue';
  65 + import { defHttp } from '/@/utils/http/axios';
  66 + import { useMessage } from '/@/hooks/web/useMessage';
  67 + import moment from 'moment';
  68 + <#include "/common/form/native/vue3NativeImport.ftl">
  69 + import { getValueType } from '/@/utils';
  70 + import { validateDuplicateValue } from '/@/utils/helper/validator';
  71 + import {loadTreeData, saveOrUpdateDict} from '../${entityName}.api';
  72 + import { Form } from 'ant-design-vue';
  73 + <#if hasOnlyValidate == true>
  74 + import { duplicateValidate } from '/@/utils/helper/validator'
  75 + </#if>
  76 +
  77 + const useForm = Form.useForm;
  78 + const formRef = ref();
  79 + const isUpdate = ref(true);
  80 + const expandedRowKeys = ref([]);
  81 + const treeData = ref([]);
  82 + const pidField = ref<string>('pid');
  83 + const emit = defineEmits(['register', 'ok']);
  84 + let model: Nullable<Recordable> = null;
  85 + const formData = reactive<Record<string, any>>({
  86 +<#list columns as po>
  87 + <#if po.isShow == 'Y'>
  88 + <#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  89 + ${po.fieldName}: undefined,
  90 + <#elseif po.fieldDbType=='Blob'>
  91 + ${po.fieldName}String: '',
  92 + <#else>
  93 + ${po.fieldName}: '',
  94 + </#if>
  95 + </#if>
  96 +</#list>
  97 + });
  98 + const { createMessage } = useMessage();
  99 + const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
  100 + const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
  101 + const confirmLoading = ref<boolean>(false);
  102 + //表单验证
  103 + const validatorRules = {
  104 + <#include "/common/validatorRulesTemplate/native/vue3MainNative.ftl">
  105 + };
  106 + const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: true });
  107 + const props = defineProps({
  108 + disabled: { type: Boolean, default: false },
  109 + });
  110 +
  111 + /**
  112 + * 新增
  113 + */
  114 + function add(obj = {}) {
  115 + edit(obj);
  116 + }
  117 +
  118 + /**
  119 + * 编辑
  120 + */
  121 + function edit(record) {
  122 + nextTick(async () => {
  123 + resetFields();
  124 + expandedRowKeys.value = [];
  125 + treeData.value = await loadTreeData({ async: false, pcode: '' });
  126 + //赋值
  127 + Object.assign(formData, record);
  128 + });
  129 + }
  130 +
  131 + /**
  132 + * 根据pid获取展开的节点
  133 + * @param pid
  134 + * @param arr
  135 + */
  136 + function getExpandKeysByPid(pid, arr) {
  137 + if (pid && arr && arr.length > 0) {
  138 + for (let i = 0; i < arr.length; i++) {
  139 + if (arr[i].key == pid && unref(expandedRowKeys).indexOf(pid) < 0) {
  140 + expandedRowKeys.value.push(arr[i].key);
  141 + getExpandKeysByPid(arr[i]['parentId'], unref(treeData));
  142 + } else {
  143 + getExpandKeysByPid(pid, arr[i].children);
  144 + }
  145 + }
  146 + }
  147 + }
  148 +
  149 + /**
  150 + * 提交数据
  151 + */
  152 + async function submitForm() {
  153 + // 触发表单验证
  154 + await validate();
  155 + confirmLoading.value = true;
  156 + const isUpdate = ref<boolean>(false);
  157 + //时间格式化
  158 + if (formData.id) {
  159 + isUpdate.value = true;
  160 + }
  161 + //循环数据
  162 + for (let data in formData) {
  163 + //如果该数据是数组并且是字符串类型
  164 + if (formData[data] instanceof Array) {
  165 + let valueType = getValueType(formRef.value.getProps, data);
  166 + //如果是字符串类型的需要变成以逗号分割的字符串
  167 + if (valueType === 'string') {
  168 + formData[data] = formData[data].join(',');
  169 + }
  170 + }
  171 + }
  172 + await saveOrUpdateDict(formData, isUpdate.value)
  173 + .then(async (res) => {
  174 + if (res.success) {
  175 + await getExpandKeysByPid(formData['${pidFieldName}'], unref(treeData));
  176 + emit('ok', {
  177 + isUpdate: unref(isUpdate),
  178 + values: { ...formData },
  179 + expandedArr: unref(expandedRowKeys).reverse(),
  180 + // 是否更改了父级节点
  181 + changeParent: model != null && model['${pidFieldName}'] != formData['${pidFieldName}'],
  182 + });
  183 + createMessage.success(res.message);
  184 + } else {
  185 + createMessage.warning(res.message);
  186 + }
  187 + })
  188 + .finally(() => {
  189 + confirmLoading.value = false;
  190 + });
  191 + }
  192 +
  193 + <#if need_popup>
  194 + /**
  195 + * popup组件值改变事件
  196 + */
  197 + function setFieldsValue(map) {
  198 + Object.keys(map).map((key) => {
  199 + formData[key] = map[key];
  200 + });
  201 + }
  202 + </#if>
  203 +
  204 + <#if need_category || need_select_tree>
  205 + /**
  206 + * 值改变事件触发
  207 + * @param key
  208 + * @param value
  209 + */
  210 + function handleFormChange(key, value) {
  211 + formData[key] = value;
  212 + }
  213 + </#if>
  214 +
  215 + <#list columns as po>
  216 + <#if po.isShow == 'Y' && po.fieldValidType?default("") == 'only'>
  217 + async function ${po.fieldName}Duplicatevalidate(_r, value) {
  218 + return duplicateValidate('${tableName}', '${po.fieldDbName}', value, formData.id || '')
  219 + }
  220 + </#if>
  221 + </#list>
  222 + defineExpose({
  223 + add,
  224 + edit,
  225 + submitForm,
  226 + });
  227 +</script>
  228 +
  229 +<style lang="less" scoped>
  230 + .antd-modal-form {
  231 + height: 500px !important;
  232 + overflow-y: auto;
  233 + padding: 24px 24px 24px 24px;
  234 + }
  235 +</style>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3Native/components/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <a-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
  3 + <${entityName}Form ref="registerForm" @ok="submitCallback" :disabled="disableSubmit"></${entityName}Form>
  4 + </a-modal>
  5 +</template>
  6 +
  7 +<script lang="ts" setup>
  8 + import { ref, nextTick, defineExpose } from 'vue';
  9 + import ${entityName}Form from './${entityName}Form.vue'
  10 +
  11 + const title = ref<string>('');
  12 + const width = ref<number>(800);
  13 + const visible = ref<boolean>(false);
  14 + const disableSubmit = ref<boolean>(false);
  15 + const registerForm = ref();
  16 + const emit = defineEmits(['register', 'success']);
  17 +
  18 + /**
  19 + * 新增
  20 + */
  21 + function add(obj={}) {
  22 + title.value = '新增';
  23 + visible.value = true;
  24 + nextTick(() => {
  25 + registerForm.value.add(obj);
  26 + });
  27 + }
  28 +
  29 + /**
  30 + * 编辑
  31 + * @param record
  32 + */
  33 + function edit(record) {
  34 + title.value = disableSubmit.value ? '详情' : '编辑';
  35 + visible.value = true;
  36 + nextTick(() => {
  37 + registerForm.value.edit(record);
  38 + });
  39 + }
  40 +
  41 + /**
  42 + * 确定按钮点击事件
  43 + */
  44 + function handleOk() {
  45 + registerForm.value.submitForm();
  46 + }
  47 +
  48 + /**
  49 + * form保存回调事件
  50 + */
  51 + function submitCallback({ isUpdate, values, expandedArr, changeParent }) {
  52 + handleCancel();
  53 + emit('success', {
  54 + isUpdate: isUpdate,
  55 + values: values,
  56 + expandedArr: expandedArr,
  57 + // 是否更改了父级节点
  58 + changeParent: changeParent,
  59 + });
  60 + }
  61 +
  62 + /**
  63 + * 取消按钮回调事件
  64 + */
  65 + function handleCancel() {
  66 + visible.value = false;
  67 + }
  68 +
  69 + defineExpose({
  70 + add,
  71 + edit,
  72 + disableSubmit,
  73 + });
  74 +</script>
  75 +
  76 +<style>
  77 + /**隐藏样式-modal确定按钮 */
  78 + .jee-hidden {
  79 + display: none !important;
  80 + }
  81 +</style>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
  1 +<#include "/common/utils.ftl">
1 2 package ${bussiPackage}.${entityPackage}.entity;
2 3  
3 4 import java.io.Serializable;
... ... @@ -31,9 +32,9 @@ public class ${entityName} implements Serializable {
31 32 <#-- 生成字典Code -->
32 33 <#assign list_field_dictCode="">
33 34 <#if po.classType='sel_user'>
34   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  35 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
35 36 <#elseif po.classType='sel_depart'>
36   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  37 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
37 38 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
38 39 <#if po.dictTable?default("")?trim?length gt 1>
39 40 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/entity/[1-n]Entity.javai
  1 +<#include "/common/utils.ftl">
1 2 <#list subTables as subTab>
2 3 #segment#${subTab.entityName}.java
3 4 package ${bussiPackage}.${entityPackage}.entity;
... ... @@ -65,7 +66,8 @@ public class ${subTab.entityName} implements Serializable {
65 66 @Dict(dicCode = "id",dicText = "name",dictTable = "sys_category")
66 67 </#if>
67 68 <#if po.classType =='sel_depart'>
68   - @Dict(dicCode = "id",dicText = "depart_name",dictTable = "sys_depart")
  69 + <#assign list_field_dictCode='dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dictTable = "sys_depart"'>
  70 + @Dict(${list_field_dictCode})
69 71 </#if>
70 72 <#-- 大字段转换 -->
71 73 <#include "/common/blob.ftl">
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue/${entityName}List.vuei
... ... @@ -193,7 +193,7 @@
193 193 <#assign sub_seq=1>
194 194 <#list subTables as sub>
195 195 <a-tab-pane tab="${sub.ftlDescription}" key="${sub_seq}" <#if sub_seq gt 1>forceRender</#if>>
196   - <${sub.entityName}List :mainId="selectedMainId" />
  196 + <${sub.entityName}List :mainId="${sub.entityName?uncap_first}MainId" />
197 197 </a-tab-pane>
198 198 <#assign sub_seq=sub_seq+1>
199 199 </#list>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Modal.vuei
... ... @@ -44,7 +44,7 @@
44 44 @input="popupCallback"
45 45 <#if po.readonly=='Y'>disabled</#if>/>
46 46 <#elseif po.classType =='sel_depart'>
47   - <j-select-depart v-model="model.${po.fieldName}" multi <#if po.readonly=='Y'>disabled</#if>/>
  47 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
48 48 <#elseif po.classType =='switch'>
49 49 <j-switch v-model="model.${po.fieldName}" <#if po.dictField!= 'is_open'>:options="${po.dictField}"</#if> <#if po.readonly=='Y'>disabled</#if>></j-switch>
50 50 <#elseif po.classType =='pca'>
... ... @@ -54,7 +54,7 @@
54 54 <#elseif po.classType =='password'>
55 55 <a-input-password v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
56 56 <#elseif po.classType =='sel_user'>
57   - <j-select-user-by-dep v-model="model.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if>/>
  57 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
58 58 <#elseif po.classType =='textarea'>
59 59 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
60 60 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/[1-n]Modal.vuei
... ... @@ -46,7 +46,7 @@
46 46 @input="popupCallback"
47 47 <#if po.readonly=='Y'>disabled</#if>/>
48 48 <#elseif po.classType =='sel_depart'>
49   - <j-select-depart v-model="model.${po.fieldName}"multi <#if po.readonly=='Y'>disabled</#if>/>
  49 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
50 50 <#elseif po.classType =='switch'>
51 51 <j-switch v-model="model.${po.fieldName}"<#if po.dictField!= 'is_open'>:options="${po.dictField}"</#if> <#if po.readonly=='Y'>disabled</#if>></j-switch>
52 52 <#elseif po.classType =='pca'>
... ... @@ -56,7 +56,7 @@
56 56 <#elseif po.classType =='password'>
57 57 <a-input-password v-model="model.${po.fieldName}"placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
58 58 <#elseif po.classType =='sel_user'>
59   - <j-select-user-by-dep v-model="model.${po.fieldName}"<#if po.readonly=='Y'>disabled</#if>/>
  59 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
60 60 <#elseif po.classType =='textarea'>
61 61 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
62 62 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei
... ... @@ -46,6 +46,10 @@
46 46 <template #htmlSlot="{text}">
47 47 <div v-html="text"></div>
48 48 </template>
  49 + <!--省市区字段回显插槽-->
  50 + <template #pcaSlot="{text}">
  51 + {{ getAreaTextByCode(text) }}
  52 + </template>
49 53 <template #fileSlot="{text}">
50 54 <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
51 55 <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
... ... @@ -77,6 +81,10 @@
77 81 </#list>
78 82 import {columns, searchFormSchema} from './${entityName}.data';
79 83 import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName}.api';
  84 + import {downloadFile} from '/@/utils/common/renderUtils';
  85 + <#if list_need_pca>
  86 + import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
  87 + </#if>
80 88 <#if list_need_category>
81 89 import { loadCategoryData } from '/@/api/common/api'
82 90 import { getAuthCache, setAuthCache } from '/@/utils/auth';
... ... @@ -94,6 +102,17 @@
94 102 rowSelection: {type: 'radio'},
95 103 formConfig: {
96 104 schemas: searchFormSchema,
  105 + fieldMapToNumber: [
  106 + <#list columns as po>
  107 + <#if po.isQuery=='Y'>
  108 + <#if po.queryMode!='single'>
  109 + <#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  110 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end']],
  111 + </#if>
  112 + </#if>
  113 + </#if>
  114 + </#list>
  115 + ],
97 116 fieldMapToTime: [
98 117 <#list columns as po>
99 118 <#if po.isQuery=='Y'>
... ... @@ -108,14 +127,15 @@
108 127 </#list>
109 128 ],
110 129 },
111   - actionColumn: {
  130 + actionColumn: {
112 131 width: 120,
113   - },
114   - pagination:{
115   - current: 1,
116   - pageSize: 5,
117   - pageSizeOptions: ['5', '10', '20'],
118   - }
  132 + fixed:'right'
  133 + },
  134 + pagination:{
  135 + current: 1,
  136 + pageSize: 5,
  137 + pageSizeOptions: ['5', '10', '20'],
  138 + }
119 139 },
120 140 exportConfig: {
121 141 name:"${tableVo.ftlDescription}",
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi
  1 +<#include "/common/utils.ftl">
1 2 import {BasicColumn} from '/@/components/Table';
2 3 import {FormSchema} from '/@/components/Table';
3 4 import { rules} from '/@/utils/helper/validator';
... ... @@ -24,7 +25,7 @@ export const columns: BasicColumn[] = [
24 25 slots: { customRender: 'htmlSlot' },
25 26 <#elseif po.classType=='pca'>
26 27 dataIndex: '${po.fieldName}',
27   - slots: { customRender: 'pcaSlot' },//TODO 未翻译
  28 + slots: { customRender: 'pcaSlot' },
28 29 <#elseif po.classType=='file'>
29 30 dataIndex: '${po.fieldName}',
30 31 slots: { customRender: 'fileSlot' },
... ... @@ -91,17 +92,23 @@ export const searchFormSchema: FormSchema[] = [
91 92 <#elseif po.classType=='switch'>
92 93 component: 'JSwitch',
93 94 componentProps:{
  95 + query:true,
94 96 <#if po.dictField != 'is_open'>
95   - options:"${po.dictField}"
  97 + options:${po.dictField}
96 98 </#if>
97 99 },
98 100 <#elseif po.classType=='sel_depart'>
99 101 component: 'JSelectDept',
100 102 <#elseif po.classType=='list_multi'>
101   - component: 'JMultiSelectTag',//暂无该组件
  103 + component: 'JSelectMultiple',
102 104 componentProps:{
103   - dictCode:"query_field_dictCode?default("")"
104   - },
  105 + <#if po.dictTable?default("")?trim?length gt 1>
  106 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}",
  107 + <#elseif po.dictField?default("")?trim?length gt 1>
  108 + dictCode:"${po.dictField}",
  109 + </#if>
  110 + triggerChange: true
  111 + },
105 112 <#elseif po.classType=='cat_tree'>
106 113 component: 'JCategorySelect',
107 114 componentProps:{
... ... @@ -117,16 +124,7 @@ export const searchFormSchema: FormSchema[] = [
117 124 <#elseif po.classType=='pca'>
118 125 component: 'JAreaLinkage',
119 126 <#elseif po.classType=='popup'>
120   - component: 'JPopup',
121   - componentProps: ({ formActionType }) => {
122   - const {setFieldsValue} = formActionType;
123   - return{
124   - setFieldsValue:setFieldsValue,
125   - code:"${po.dictTable}",
126   - fieldConfig:"${po.dictField}",
127   - multi:${po.extendParams.popupMulti?c},
128   - }
129   - },
  127 + <#include "/common/form/vue3popup.ftl">
130 128 <#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
131 129 <#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
132 130 component: 'JDictSelectTag',
... ... @@ -153,6 +151,8 @@ export const searchFormSchema: FormSchema[] = [
153 151 componentProps: {
154 152 showTime:true
155 153 },
  154 +<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  155 + component: 'JRangeNumber',
156 156 <#else>
157 157 component: 'Input', //TODO 范围查询
158 158 </#if>
... ... @@ -186,27 +186,22 @@ export const formSchema: FormSchema[] = [
186 186 </#if>
187 187 {
188 188 label: '${po.filedComment}',
189   - field: '${po.fieldName}',
  189 + field: ${autoStringSuffix(po)},
190 190 <#if po.classType =='date'>
191 191 component: 'DatePicker',
192   - <#elseif po.fieldType =='datetime'>
  192 + <#elseif po.classType =='datetime'>
193 193 component: 'DatePicker',
194 194 componentProps: {
195   - showTime:true
  195 + showTime:true,
  196 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
196 197 },
197   - <#elseif po.fieldType =='time'>
  198 + <#elseif po.classType =='time'>
198 199 component: 'TimePicker',
  200 + componentProps: {
  201 + valueFormat: 'HH:mm:ss'
  202 + },
199 203 <#elseif po.classType =='popup'>
200   - component: 'JPopup',
201   - componentProps: ({ formActionType }) => {
202   - const {setFieldsValue} = formActionType;
203   - return{
204   - setFieldsValue:setFieldsValue,
205   - code:"${po.dictTable}",
206   - fieldConfig:${po.dictField},
207   - multi:${po.extendParams.popupMulti?c},
208   - }
209   - },
  204 + <#include "/common/form/vue3popup.ftl">
210 205 <#elseif po.classType =='sel_depart'>
211 206 component: 'JSelectDept',
212 207 <#elseif po.classType =='switch'>
... ... @@ -235,7 +230,7 @@ export const formSchema: FormSchema[] = [
235 230 dictCode:"${form_field_dictCode}"
236 231 },
237 232 <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
238   - component: 'JMultiSelectTag',//TODO 暂无该组件
  233 + component: 'JSelectMultiple',
239 234 componentProps:{
240 235 dictCode:"${form_field_dictCode}"
241 236 },
... ... @@ -270,7 +265,7 @@ export const formSchema: FormSchema[] = [
270 265 </#if>
271 266 },
272 267 <#elseif po.classType=='umeditor'>
273   - component: 'JCodeEditor', //TODO String后缀暂未添加
  268 + component: 'JEditor',
274 269 <#elseif po.classType == 'sel_tree'>
275 270 component: 'JTreeSelect',
276 271 componentProps:{
... ... @@ -302,16 +297,16 @@ export const formSchema: FormSchema[] = [
302 297 </#if>
303 298 <#-- 唯一校验 -->
304 299 <#if fieldValidType == 'only'>
305   - {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  300 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
306 301 <#-- 6到16位数字 -->
307 302 <#elseif fieldValidType == 'n6-16'>
308 303 { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
309 304 <#-- 6到16位任意字符 -->
310 305 <#elseif fieldValidType == '*6-16'>
311 306 { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
312   - <#-- 6到18位字符串 -->
  307 + <#-- 6到18位字 -->
313 308 <#elseif fieldValidType == 's6-18'>
314   - { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  309 + { pattern:/^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
315 310 <#-- 网址 -->
316 311 <#elseif fieldValidType == 'url'>
317 312 { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
... ... @@ -387,7 +382,7 @@ export const ${sub.entityName?uncap_first}Columns: BasicColumn[] = [
387 382 slots: { customRender: 'htmlSlot' },
388 383 <#elseif po.classType=='pca'>
389 384 dataIndex: '${po.fieldName}',
390   - slots: { customRender: 'pcaSlot' },//TODO 未翻译
  385 + slots: { customRender: 'pcaSlot' },
391 386 <#elseif po.classType=='file'>
392 387 dataIndex: '${po.fieldName}',
393 388 slots: { customRender: 'fileSlot' },
... ... @@ -419,7 +414,7 @@ export const ${sub.entityName?uncap_first}Columns: BasicColumn[] = [
419 414 return render.renderCategoryTree(text,'${po.dictField?default("")}')
420 415 },
421 416 <#else>
422   - customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  417 + customRender: ({text, record}) => (text ? record['${po.dictText}'] : '')
423 418 </#if>
424 419 <#else>
425 420 dataIndex: '${po.fieldName}'
... ... @@ -452,27 +447,22 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
452 447 </#if>
453 448 {
454 449 label: '${po.filedComment}',
455   - field: '${po.fieldName}',
  450 + field: ${autoStringSuffix(po)},
456 451 <#if po.classType =='date'>
457 452 component: 'DatePicker',
458   - <#elseif po.fieldType =='datetime'>
  453 + <#elseif po.classType =='datetime'>
459 454 component: 'DatePicker',
460 455 componentProps: {
461   - showTime:true
  456 + showTime:true,
  457 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
462 458 },
463   - <#elseif po.fieldType =='time'>
  459 + <#elseif po.classType =='time'>
464 460 component: 'TimePicker',
  461 + componentProps: {
  462 + valueFormat: 'HH:mm:ss'
  463 + },
465 464 <#elseif po.classType =='popup'>
466   - component: 'JPopup',
467   - componentProps: ({ formActionType }) => {
468   - const {setFieldsValue} = formActionType;
469   - return{
470   - setFieldsValue:setFieldsValue,
471   - code:"${po.dictTable}",
472   - fieldConfig:${po.dictField},
473   - multi:${po.extendParams.popupMulti?c},
474   - }
475   - },
  465 + <#include "/common/form/vue3popup.ftl">
476 466 <#elseif po.classType =='sel_depart'>
477 467 component: 'JSelectDept',
478 468 <#elseif po.classType =='switch'>
... ... @@ -494,14 +484,14 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
494 484 labelKey:'realname',
495 485 },
496 486 <#elseif po.classType =='textarea'>
497   - component: 'InputTextArea',//TODO 注意string转换问题
  487 + component: 'InputTextArea',
498 488 <#elseif po.classType=='list' || po.classType=='radio'>
499 489 component: 'JDictSelectTag',
500 490 componentProps:{
501 491 dictCode:"${form_field_dictCode}"
502 492 },
503 493 <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
504   - component: 'JMultiSelectTag',//TODO 暂无该组件
  494 + component: 'JSelectMultiple',
505 495 componentProps:{
506 496 dictCode:"${form_field_dictCode}"
507 497 },
... ... @@ -536,7 +526,7 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
536 526 </#if>
537 527 },
538 528 <#elseif po.classType=='umeditor'>
539   - component: 'JCodeEditor', //TODO String后缀暂未添加
  529 + component: 'JEditor',
540 530 <#elseif po.classType == 'sel_tree'>
541 531 component: 'JTreeSelect',
542 532 componentProps:{
... ... @@ -568,16 +558,16 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
568 558 </#if>
569 559 <#-- 唯一校验 -->
570 560 <#if fieldValidType == 'only'>
571   - {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  561 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
572 562 <#-- 6到16位数字 -->
573 563 <#elseif fieldValidType == 'n6-16'>
574 564 { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
575 565 <#-- 6到16位任意字符 -->
576 566 <#elseif fieldValidType == '*6-16'>
577 567 { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
578   - <#-- 6到18位字符串 -->
  568 + <#-- 6到18位字 -->
579 569 <#elseif fieldValidType == 's6-18'>
580   - { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  570 + { pattern: /^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
581 571 <#-- 网址 -->
582 572 <#elseif fieldValidType == 'url'>
583 573 { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/[1-n]List.vuei
... ... @@ -51,6 +51,7 @@
51 51 import {${sub.entityName?uncap_first}List, ${sub.entityName?uncap_first}Delete, ${sub.entityName?uncap_first}DeleteBatch, ${sub.entityName?uncap_first}ExportXlsUrl, ${sub.entityName?uncap_first}ImportUrl } from './${entityName}.api';
52 52 import {isEmpty} from "/@/utils/is";
53 53 import {useMessage} from '/@/hooks/web/useMessage';
  54 + import {downloadFile} from '/@/utils/common/renderUtils';
54 55  
55 56 //接收主表id
56 57 const mainId = inject('mainId') || '';
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei
  1 +<#include "/common/utils.ftl">
1 2 <template>
2   - <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  3 + <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="${getModalWidth(tableVo.fieldRowNum?default(1))}" @ok="handleSubmit">
3 4 <BasicForm @register="registerForm"/>
4 5 </BasicModal>
5 6 </template>
... ... @@ -18,6 +19,7 @@
18 19 labelWidth: 150,
19 20 schemas: formSchema,
20 21 showActionButtonGroup: false,
  22 + baseColProps: {span: ${getFormSpan(tableVo.fieldRowNum?default(1))}}
21 23 });
22 24 //表单赋值
23 25 const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
... ... @@ -54,5 +56,12 @@
54 56 </script>
55 57  
56 58 <style lang="less" scoped>
  59 + /** 时间和数字输入框样式 */
  60 + :deep(.ant-input-number){
  61 + width: 100%
  62 + }
57 63  
  64 + :deep(.ant-calendar-picker){
  65 + width: 100%
  66 + }
58 67 </style>
59 68 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/[1-n]Modal.vuei
... ... @@ -2,7 +2,7 @@
2 2 <#list subTables as sub>
3 3 #segment#${sub.entityName}Modal.vue
4 4 <template>
5   - <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  5 + <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="${getModalWidth(tableVo.fieldRowNum?default(1))}" @ok="handleSubmit">
6 6 <BasicForm @register="registerForm"/>
7 7 </BasicModal>
8 8 </template>
... ... @@ -24,6 +24,7 @@
24 24 labelWidth: 150,
25 25 schemas: ${sub.entityName?uncap_first}FormSchema,
26 26 showActionButtonGroup: false,
  27 + baseColProps: {span: ${getFormSpan(tableVo.fieldRowNum?default(1))}}
27 28 });
28 29 //表单赋值
29 30 const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
... ... @@ -63,4 +64,14 @@
63 64 }
64 65 }
65 66 </script>
  67 +<style lang="less" scoped>
  68 + /** 时间和数字输入框样式 */
  69 + :deep(.ant-input-number){
  70 + width: 100%
  71 + }
  72 +
  73 + :deep(.ant-calendar-picker){
  74 + width: 100%
  75 + }
  76 +</style>
66 77 </#list>
67 78 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -200,17 +200,14 @@ public class ${entityName}Controller {
200 200 QueryWrapper<${entityName}> queryWrapper = QueryGenerator.initQueryWrapper(${entityName?uncap_first}, request.getParameterMap());
201 201 LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
202 202  
203   - //Step.2 获取导出数据
204   - List<${entityName}> queryList = ${entityName?uncap_first}Service.list(queryWrapper);
205   - // 过滤选中数据
  203 + //配置选中数据查询条件
206 204 String selections = request.getParameter("selections");
207   - List<${entityName}> ${entityName?uncap_first}List = new ArrayList<${entityName}>();
208   - if(oConvertUtils.isEmpty(selections)) {
209   - ${entityName?uncap_first}List = queryList;
210   - }else {
211   - List<String> selectionList = Arrays.asList(selections.split(","));
212   - ${entityName?uncap_first}List = queryList.stream().filter(item -> selectionList.contains(item.getId())).collect(Collectors.toList());
  205 + if(oConvertUtils.isNotEmpty(selections)) {
  206 + List<String> selectionList = Arrays.asList(selections.split(","));
  207 + queryWrapper.in("id",selectionList);
213 208 }
  209 + //Step.2 获取导出数据
  210 + List<${entityName}> ${entityName?uncap_first}List = ${entityName?uncap_first}Service.list(queryWrapper);
214 211  
215 212 // Step.3 组装pageList
216 213 List<${entityName}Page> pageList = new ArrayList<${entityName}Page>();
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
  1 +<#include "/common/utils.ftl">
1 2 package ${bussiPackage}.${entityPackage}.entity;
2 3  
3 4 import java.io.Serializable;
... ... @@ -31,9 +32,9 @@ public class ${entityName} implements Serializable {
31 32 <#-- 生成字典Code -->
32 33 <#assign list_field_dictCode="">
33 34 <#if po.classType='sel_user'>
34   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  35 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
35 36 <#elseif po.classType='sel_depart'>
36   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  37 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
37 38 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
38 39 <#if po.dictTable?default("")?trim?length gt 1>
39 40 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/entity/[1-n]Entity.javai
  1 +<#include "/common/utils.ftl">
1 2 <#list subTables as subTab>
2 3 #segment#${subTab.entityName}.java
3 4 package ${bussiPackage}.${entityPackage}.entity;
... ... @@ -33,9 +34,9 @@ public class ${subTab.entityName} implements Serializable {
33 34 <#-- 生成字典Code -->
34 35 <#assign list_field_dictCode="">
35 36 <#if po.classType='sel_user'>
36   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  37 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
37 38 <#elseif po.classType='sel_depart'>
38   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  39 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
39 40 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
40 41 <#if po.dictTable?default("")?trim?length gt 1>
41 42 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Form.vuei
... ... @@ -41,7 +41,7 @@
41 41 @input="popupCallback"
42 42 <#if po.readonly=='Y'>disabled</#if>/>
43 43 <#elseif po.classType =='sel_depart'>
44   - <j-select-depart v-model="model.${po.fieldName}" multi <#if po.readonly=='Y'>disabled</#if>/>
  44 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
45 45 <#elseif po.classType =='switch'>
46 46 <j-switch v-model="model.${po.fieldName}" <#if po.dictField!= 'is_open'>:options="${po.dictField}"</#if> <#if po.readonly=='Y'>disabled</#if>></j-switch>
47 47 <#elseif po.classType =='pca'>
... ... @@ -51,7 +51,7 @@
51 51 <#elseif po.classType =='password'>
52 52 <a-input-password v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
53 53 <#elseif po.classType =='sel_user'>
54   - <j-select-user-by-dep v-model="model.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if>/>
  54 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
55 55 <#elseif po.classType =='textarea'>
56 56 <a-textarea v-decorator="[${autoStringSuffix(po)}${autoWriteRules(po)}]" rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
57 57 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/[1-n]Form.vuei
... ... @@ -36,9 +36,9 @@
36 36 <#elseif po.classType =='switch'>
37 37 <j-switch v-model="model.${po.fieldName}" <#if po.dictField!= 'is_open'>:options="${po.dictField}"</#if>></j-switch>
38 38 <#elseif po.classType =='sel_depart'>
39   - <j-select-depart v-model="model.${po.fieldName}" />
  39 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
40 40 <#elseif po.classType =='sel_user'>
41   - <j-select-user-by-dep v-model="model.${po.fieldName}" />
  41 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
42 42 <#elseif po.classType =='textarea'>
43 43 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}"/>
44 44 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei
... ... @@ -60,6 +60,10 @@
60 60 <template #htmlSlot="{text}">
61 61 <div v-html="text"></div>
62 62 </template>
  63 + <!--省市区字段回显插槽-->
  64 + <template #pcaSlot="{text}">
  65 + {{ getAreaTextByCode(text) }}
  66 + </template>
63 67 <template #fileSlot="{text}">
64 68 <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
65 69 <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
... ... @@ -81,6 +85,10 @@
81 85 </#list>
82 86 import {columns, searchFormSchema} from './${entityName}.data';
83 87 import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName}.api';
  88 + import {downloadFile} from '/@/utils/common/renderUtils';
  89 + <#if list_need_pca>
  90 + import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
  91 + </#if>
84 92 <#if list_need_category>
85 93 import { loadCategoryData } from '/@/api/common/api'
86 94 import { getAuthCache, setAuthCache } from '/@/utils/auth';
... ... @@ -102,6 +110,17 @@
102 110 schemas: searchFormSchema,
103 111 autoSubmitOnEnter:true,
104 112 showAdvancedButton:true,
  113 + fieldMapToNumber: [
  114 + <#list columns as po>
  115 + <#if po.isQuery=='Y'>
  116 + <#if po.queryMode!='single'>
  117 + <#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  118 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end']],
  119 + </#if>
  120 + </#if>
  121 + </#if>
  122 + </#list>
  123 + ],
105 124 fieldMapToTime: [
106 125 <#list columns as po>
107 126 <#if po.isQuery=='Y'>
... ... @@ -116,9 +135,10 @@
116 135 </#list>
117 136 ],
118 137 },
119   - actionColumn: {
  138 + actionColumn: {
120 139 width: 120,
121   - },
  140 + fixed:'right'
  141 + },
122 142 },
123 143 exportConfig: {
124 144 name:"${tableVo.ftlDescription}",
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi
  1 +<#include "/common/utils.ftl">
1 2 import {BasicColumn} from '/@/components/Table';
2 3 import {FormSchema} from '/@/components/Table';
3 4 import { rules} from '/@/utils/helper/validator';
... ... @@ -25,7 +26,7 @@ export const columns: BasicColumn[] = [
25 26 slots: { customRender: 'htmlSlot' },
26 27 <#elseif po.classType=='pca'>
27 28 dataIndex: '${po.fieldName}',
28   - slots: { customRender: 'pcaSlot' },//TODO 未翻译
  29 + slots: { customRender: 'pcaSlot' },
29 30 <#elseif po.classType=='file'>
30 31 dataIndex: '${po.fieldName}',
31 32 slots: { customRender: 'fileSlot' },
... ... @@ -57,7 +58,7 @@ export const columns: BasicColumn[] = [
57 58 return render.renderCategoryTree(text,'${po.dictField?default("")}')
58 59 },
59 60 <#else>
60   - customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  61 + customRender: ({text, record}) => (text ? record['${po.dictText}'] : '')
61 62 </#if>
62 63 <#else>
63 64 dataIndex: '${po.fieldName}'
... ... @@ -84,7 +85,7 @@ export const searchFormSchema: FormSchema[] = [
84 85 <#if po.queryMode=='single'>
85 86 {
86 87 label: "${po.filedComment}",
87   - field: "${po.fieldName}",
  88 + field: ${autoStringSuffix(po)},
88 89 <#if po.classType=='sel_search'>
89 90 component: 'JSearchSelect',
90 91 componentProps:{
... ... @@ -95,16 +96,22 @@ export const searchFormSchema: FormSchema[] = [
95 96 <#elseif po.classType=='switch'>
96 97 component: 'JSwitch',
97 98 componentProps:{
  99 + query:true,
98 100 <#if po.dictField != 'is_open'>
99   - options:"${po.dictField}"
  101 + options:${po.dictField}
100 102 </#if>
101 103 },
102 104 <#elseif po.classType=='sel_depart'>
103 105 component: 'JSelectDept',
104 106 <#elseif po.classType=='list_multi'>
105   - component: 'JMultiSelectTag',//暂无该组件
106   - componentProps:{
107   - dictCode:"query_field_dictCode?default("")"
  107 + component: 'JSelectMultiple',
  108 + componentProps:{
  109 + <#if po.dictTable?default("")?trim?length gt 1>
  110 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}",
  111 + <#elseif po.dictField?default("")?trim?length gt 1>
  112 + dictCode:"${po.dictField}",
  113 + </#if>
  114 + triggerChange: true
108 115 },
109 116 <#elseif po.classType=='cat_tree'>
110 117 component: 'JCategorySelect',
... ... @@ -121,16 +128,7 @@ export const searchFormSchema: FormSchema[] = [
121 128 <#elseif po.classType=='pca'>
122 129 component: 'JAreaLinkage',
123 130 <#elseif po.classType=='popup'>
124   - component: 'JPopup',
125   - componentProps: ({ formActionType }) => {
126   - const {setFieldsValue} = formActionType;
127   - return{
128   - setFieldsValue:setFieldsValue,
129   - code:"${po.dictTable}",
130   - fieldConfig:"${po.dictField}",
131   - multi:${po.extendParams.popupMulti?c},
132   - }
133   - },
  131 + <#include "/common/form/vue3popup.ftl">
134 132 <#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
135 133 <#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
136 134 component: 'JDictSelectTag',
... ... @@ -157,6 +155,8 @@ export const searchFormSchema: FormSchema[] = [
157 155 componentProps: {
158 156 showTime:true
159 157 },
  158 +<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  159 + component: 'JRangeNumber',
160 160 <#else>
161 161 component: 'Input', //TODO 范围查询
162 162 </#if>
... ... @@ -189,28 +189,23 @@ export const formSchema: FormSchema[] = [
189 189 </#if>
190 190 {
191 191 label: '${po.filedComment}',
192   - field: '${po.fieldName}',
  192 + field: ${autoStringSuffix(po)},
193 193 <#if po.classType =='date'>
194 194 component: 'DatePicker',
195   - <#elseif po.fieldType =='datetime'>
  195 + <#elseif po.classType =='datetime'>
196 196 component: 'DatePicker',
197 197 componentProps: {
198   - showTime:true
  198 + showTime:true,
  199 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
199 200 },
200   - <#elseif po.fieldType =='time'>
  201 + <#elseif po.classType =='time'>
201 202 component: 'TimePicker',
  203 + componentProps: {
  204 + valueFormat: 'HH:mm:ss'
  205 + },
202 206 <#elseif po.classType =='popup'>
203   - component: 'JPopup',
204   - componentProps: ({ formActionType }) => {
205   - const {setFieldsValue} = formActionType;
206   - return{
207   - setFieldsValue:setFieldsValue,
208   - code:"${po.dictTable}",
209   - fieldConfig:${po.dictField},
210   - multi:${po.extendParams.popupMulti?c},
211   - }
212   - },
213   - <#elseif po.classType =='sel_depart'>
  207 + <#include "/common/form/vue3popup.ftl">
  208 + <#elseif po.classType =='sel_depart'>
214 209 component: 'JSelectDept',
215 210 <#elseif po.classType =='switch'>
216 211 component: 'JSwitch',
... ... @@ -231,14 +226,14 @@ export const formSchema: FormSchema[] = [
231 226 labelKey:'realname',
232 227 },
233 228 <#elseif po.classType =='textarea'>
234   - component: 'InputTextArea',//TODO 注意string转换问题
  229 + component: 'InputTextArea',
235 230 <#elseif po.classType=='list' || po.classType=='radio'>
236 231 component: 'JDictSelectTag',
237 232 componentProps:{
238 233 dictCode:"${form_field_dictCode}"
239 234 },
240 235 <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
241   - component: 'JMultiSelectTag',//TODO 暂无该组件
  236 + component: 'JSelectMultiple',
242 237 componentProps:{
243 238 dictCode:"${form_field_dictCode}"
244 239 },
... ... @@ -273,7 +268,7 @@ export const formSchema: FormSchema[] = [
273 268 </#if>
274 269 },
275 270 <#elseif po.classType=='umeditor'>
276   - component: 'JCodeEditor', //TODO String后缀暂未添加
  271 + component: 'JEditor',
277 272 <#elseif po.classType == 'sel_tree'>
278 273 component: 'JTreeSelect',
279 274 componentProps:{
... ... @@ -305,16 +300,16 @@ export const formSchema: FormSchema[] = [
305 300 </#if>
306 301 <#-- 唯一校验 -->
307 302 <#if fieldValidType == 'only'>
308   - {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  303 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
309 304 <#-- 6到16位数字 -->
310 305 <#elseif fieldValidType == 'n6-16'>
311 306 { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
312 307 <#-- 6到16位任意字符 -->
313 308 <#elseif fieldValidType == '*6-16'>
314 309 { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
315   - <#-- 6到18位字符串 -->
  310 + <#-- 6到18位字 -->
316 311 <#elseif fieldValidType == 's6-18'>
317   - { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  312 + { pattern:/^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
318 313 <#-- 网址 -->
319 314 <#elseif fieldValidType == 'url'>
320 315 { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
... ... @@ -390,7 +385,7 @@ export const ${sub.entityName?uncap_first}Columns: BasicColumn[] = [
390 385 slots: { customRender: 'htmlSlot' },
391 386 <#elseif po.classType=='pca'>
392 387 dataIndex: '${po.fieldName}',
393   - slots: { customRender: 'pcaSlot' },//TODO 未翻译
  388 + slots: { customRender: 'pcaSlot' },
394 389 <#elseif po.classType=='file'>
395 390 dataIndex: '${po.fieldName}',
396 391 slots: { customRender: 'fileSlot' },
... ... @@ -422,7 +417,7 @@ export const ${sub.entityName?uncap_first}Columns: BasicColumn[] = [
422 417 return render.renderCategoryTree(text,'${po.dictField?default("")}')
423 418 },
424 419 <#else>
425   - customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  420 + customRender: ({text, record}) => (text ? record['${po.dictText}'] : '')
426 421 </#if>
427 422 <#else>
428 423 dataIndex: '${po.fieldName}'
... ... @@ -436,10 +431,14 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
436 431 <#assign form_cat_tree = false>
437 432 <#assign form_cat_back = "">
438 433 <#assign bpm_flag=false>
  434 +<#assign sub_id_exists=false>
439 435 <#list sub.colums as po><#rt/>
440 436 <#if po.fieldDbName=='bpm_status'>
441 437 <#assign bpm_flag=true>
442 438 </#if>
  439 +<#if po.fieldDbName=='id'>
  440 + <#assign sub_id_exists=true>
  441 +</#if>
443 442 <#if po.isShow =='Y'>
444 443 <#assign form_field_dictCode="">
445 444 <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
... ... @@ -449,27 +448,22 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
449 448 </#if>
450 449 {
451 450 label: '${po.filedComment}',
452   - field: '${po.fieldName}',
  451 + field: ${autoStringSuffix(po)},
453 452 <#if po.classType =='date'>
454 453 component: 'DatePicker',
455   - <#elseif po.fieldType =='datetime'>
  454 + <#elseif po.classType =='datetime'>
456 455 component: 'DatePicker',
457 456 componentProps: {
458   - showTime:true
  457 + showTime:true,
  458 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
459 459 },
460   - <#elseif po.fieldType =='time'>
  460 + <#elseif po.classType =='time'>
461 461 component: 'TimePicker',
  462 + componentProps: {
  463 + valueFormat: 'HH:mm:ss'
  464 + },
462 465 <#elseif po.classType =='popup'>
463   - component: 'JPopup',
464   - componentProps: ({ formActionType }) => {
465   - const {setFieldsValue} = formActionType;
466   - return{
467   - setFieldsValue:setFieldsValue,
468   - code:"${po.dictTable}",
469   - fieldConfig:${po.dictField},
470   - multi:${po.extendParams.popupMulti?c},
471   - }
472   - },
  466 + <#include "/common/form/vue3popup.ftl">
473 467 <#elseif po.classType =='sel_depart'>
474 468 component: 'JSelectDept',
475 469 <#elseif po.classType =='switch'>
... ... @@ -491,14 +485,14 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
491 485 labelKey:'realname',
492 486 },
493 487 <#elseif po.classType =='textarea'>
494   - component: 'InputTextArea',//TODO 注意string转换问题
  488 + component: 'InputTextArea',
495 489 <#elseif po.classType=='list' || po.classType=='radio'>
496 490 component: 'JDictSelectTag',
497 491 componentProps:{
498 492 dictCode:"${form_field_dictCode}"
499 493 },
500 494 <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
501   - component: 'JMultiSelectTag',//TODO 暂无该组件
  495 + component: 'JSelectMultiple',
502 496 componentProps:{
503 497 dictCode:"${form_field_dictCode}"
504 498 },
... ... @@ -533,7 +527,7 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
533 527 </#if>
534 528 },
535 529 <#elseif po.classType=='umeditor'>
536   - component: 'JCodeEditor', //TODO String后缀暂未添加
  530 + component: 'JEditor',
537 531 <#elseif po.classType == 'sel_tree'>
538 532 component: 'JTreeSelect',
539 533 componentProps:{
... ... @@ -565,16 +559,16 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
565 559 </#if>
566 560 <#-- 唯一校验 -->
567 561 <#if fieldValidType == 'only'>
568   - {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  562 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
569 563 <#-- 6到16位数字 -->
570 564 <#elseif fieldValidType == 'n6-16'>
571 565 { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
572 566 <#-- 6到16位任意字符 -->
573 567 <#elseif fieldValidType == '*6-16'>
574 568 { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
575   - <#-- 6到18位字符串 -->
  569 + <#-- 6到18位字 -->
576 570 <#elseif fieldValidType == 's6-18'>
577   - { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  571 + { pattern: /^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
578 572 <#-- 网址 -->
579 573 <#elseif fieldValidType == 'url'>
580 574 { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
... ... @@ -616,6 +610,14 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
616 610 },
617 611 </#if>
618 612 </#list>
  613 +<#if sub_id_exists == false>
  614 + {
  615 + label: '',
  616 + field: 'id',
  617 + component: 'Input',
  618 + show: false
  619 + },
  620 +</#if>
619 621 ];
620 622 </#if>
621 623 </#list>
... ... @@ -642,12 +644,20 @@ export const ${sub.entityName?uncap_first}JVxeColumns: JVxeColumn[] = [
642 644 <#if col.readonly=='Y'>
643 645 disabled:true,
644 646 </#if>
  647 +<#elseif col.classType =='time'>
  648 + type: JVxeTypes.time,
  649 + <#if col.readonly=='Y'>
  650 + disabled:true,
  651 + </#if>
645 652 <#elseif col.classType =='textarea'>
646 653 type: JVxeTypes.textarea,
647 654 <#if col.readonly=='Y'>
648 655 disabled:true,
649 656 </#if>
650   -<#elseif "int,decimal,double,"?contains(col.classType)>
  657 +<#-- update-begin-author:taoyan date:20220523 for: VUEN-1084 【vue3】online表单测试发现的新问题 20、一对多列字段类型生成的不对,数字或者金额类型 -->
  658 +<#-- elseif "int,decimal,double,"?contains(col.classType) -->
  659 +<#elseif col.fieldDbType=='int' || col.fieldDbType=='double' || col.fieldDbType=='BigDecimal'>
  660 +<#-- update-end-author:taoyan date:20220523 for: VUEN-1084 【vue3】online表单测试发现的新问题 20、一对多列字段类型生成的不对,数字或者金额类型 -->
651 661 type: JVxeTypes.inputNumber,
652 662 <#if col.readonly=='Y'>
653 663 disabled:true,
... ... @@ -684,6 +694,16 @@ export const ${sub.entityName?uncap_first}JVxeColumns: JVxeColumn[] = [
684 694 <#if col.readonly=='Y'>
685 695 disabled:true,
686 696 </#if>
  697 +<#elseif col.classType =='sel_depart'>
  698 + type: JVxeTypes.departSelect,
  699 + <#if col.readonly=='Y'>
  700 + disabled:true,
  701 + </#if>
  702 +<#elseif col.classType =='sel_user'>
  703 + type: JVxeTypes.userSelect,
  704 + <#if col.readonly=='Y'>
  705 + disabled:true,
  706 + </#if>
687 707 <#elseif col.classType =='image'>
688 708 type: JVxeTypes.image,
689 709 token:true,
... ... @@ -707,9 +727,9 @@ export const ${sub.entityName?uncap_first}JVxeColumns: JVxeColumn[] = [
707 727 <#elseif col.classType =='switch'>
708 728 type: JVxeTypes.checkbox,
709 729 <#if col.dictField == 'is_open'>
710   - customValue: ['Y', 'N'],
  730 + customValue: ['Y', 'N'],
711 731 <#else>
712   - customValue: ${col.dictField},
  732 + customValue: ${col.dictField},
713 733 </#if>
714 734 <#if col.readonly=='Y'>
715 735 disabled:true,
... ... @@ -720,18 +740,11 @@ export const ${sub.entityName?uncap_first}JVxeColumns: JVxeColumn[] = [
720 740 <#else>
721 741 <#assign popupBackFields = "${col.dictText}">
722 742 </#if>
723   - type: JVxeTypes.popup,
724   - popupCode:"${col.dictTable}",
725   - field:"${col.dictField}",
726   - orgFields:"${col.dictField}",
727   - destFields:"${Format.underlineToHump(col.dictText)}",
728   - <#if col.readonly=='Y'>
729   - disabled:true,
730   - </#if>
  743 + <#include "/common/form/vue3Jvxepopup.ftl">
731 744 <#else>
732   - type: JVxeTypes.input,
  745 + type: JVxeTypes.input,
733 746 <#if col.readonly=='Y'>
734   - disabled:true,
  747 + disabled:true,
735 748 </#if>
736 749 </#if>
737 750 <#if col.classType =='list_multi' || col.classType =='checkbox'>
... ... @@ -754,18 +767,7 @@ export const ${sub.entityName?uncap_first}JVxeColumns: JVxeColumn[] = [
754 767 defaultValue:'',
755 768 </#if>
756 769 <#-- 子表的校验 -->
757   -<#assign subFieldValidType = col.fieldValidType!''>
758   -<#-- 非空校验 -->
759   -<#if col.nullable == 'N' || subFieldValidType == '*'>
760   - validateRules: [{ required: true, message: '${'$'}{title}不能为空' }],
761   -<#-- 其他情况下,只要有值就被认为是正则校验 -->
762   -<#elseif subFieldValidType?length gt 0>
763   -<#assign subMessage = '格式不正确'>
764   -<#if subFieldValidType == 'only' >
765   - <#assign subMessage = '不能重复'>
766   -</#if>
767   - validateRules: [{ pattern: "${subFieldValidType}", message: "${'$'}{title}${subMessage}" }],
768   -</#if>
  770 + <#include "/common/validatorRulesTemplate/sub-vue3.ftl">
769 771 },
770 772 </#if>
771 773 </#if>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei
  1 +<#include "/common/utils.ftl">
1 2 <template>
2   - <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  3 + <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="${getModalWidth(tableVo.fieldRowNum?default(1))}" @ok="handleSubmit">
3 4 <BasicForm @register="registerForm" ref="formRef"/>
4 5 <!-- 子表单区域 -->
5 6 <a-tabs v-model:activeKey="activeKey" @change="handleChangeTabs">
... ... @@ -7,7 +8,7 @@
7 8 <#assign refKey = sub.entityName?uncap_first/>
8 9 <#if sub.foreignRelationType =='1'>
9 10 <a-tab-pane tab="${sub.ftlDescription}" key="${refKey}" :forceRender="true">
10   - <${sub.entityName}Form ref="${sub.entityName?uncap_first}Form"></${sub.entityName}Form>
  11 + <${sub.entityName}Form ref="${sub.entityName?uncap_first}Form" :disabled="formDisabled"></${sub.entityName}Form>
11 12 </a-tab-pane>
12 13  
13 14 <#else>
... ... @@ -19,7 +20,8 @@
19 20 :loading="${sub.entityName?uncap_first}Table.loading"
20 21 :columns="${sub.entityName?uncap_first}Table.columns"
21 22 :dataSource="${sub.entityName?uncap_first}Table.dataSource"
22   - :maxHeight="300"
  23 + :height="340"
  24 + :disabled="formDisabled"
23 25 :rowNumber="true"
24 26 :rowSelection="true"
25 27 :toolbar="true"
... ... @@ -48,6 +50,7 @@
48 50 // Emits声明
49 51 const emit = defineEmits(['register','success']);
50 52 const isUpdate = ref(true);
  53 + const formDisabled = ref(false);
51 54 const refKeys = ref([<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
52 55 <#assign hasOne2Many = false>
53 56 <#assign hasOne2One = false>
... ... @@ -77,6 +80,7 @@
77 80 labelWidth: 150,
78 81 schemas: formSchema,
79 82 showActionButtonGroup: false,
  83 + baseColProps: {span: ${getFormSpan(tableVo.fieldRowNum?default(1))}}
80 84 });
81 85 //表单赋值
82 86 const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
... ... @@ -84,6 +88,7 @@
84 88 await reset();
85 89 setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
86 90 isUpdate.value = !!data?.isUpdate;
  91 + formDisabled.value = !data?.showFooter;
87 92 if (unref(isUpdate)) {
88 93 //表单赋值
89 94 await setFieldsValue({
... ... @@ -176,5 +181,12 @@
176 181 </script>
177 182  
178 183 <style lang="less" scoped>
  184 + /** 时间和数字输入框样式 */
  185 + :deep(.ant-input-number){
  186 + width: 100%
  187 + }
179 188  
  189 + :deep(.ant-calendar-picker){
  190 + width: 100%
  191 + }
180 192 </style>
181 193 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/[1-n]Form.vuei
  1 +<#include "/common/utils.ftl">
1 2 <#list subTables as sub>
2 3 <#if sub.foreignRelationType=='1'>
3 4 #segment#${sub.entityName}Form.vue
... ... @@ -15,11 +16,18 @@
15 16 name:"${sub.entityName}Form",
16 17 components: {BasicForm},
17 18 emits:['register'],
18   - setup(_,{emit}) {
19   - const [registerForm, {resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
  19 + props:{
  20 + disabled: {
  21 + type: Boolean,
  22 + default: false
  23 + }
  24 + },
  25 + setup(props,{emit}) {
  26 + const [registerForm, {setProps, resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
20 27 labelWidth: 150,
21 28 schemas: ${sub.entityName?uncap_first}FormSchema,
22 29 showActionButtonGroup: false,
  30 + baseColProps: {span: ${getFormSpan(tableVo.fieldRowNum?default(1))}}
23 31 });
24 32 /**
25 33 *初始化加载数据
... ... @@ -30,13 +38,22 @@
30 38 res.success && setFieldsValue({...res.result.records[0]});
31 39 })
32 40 }
  41 + setProps({disabled: props.disabled})
33 42 }
34   - /**
35   - *获取表单数据
36   - */
  43 +
  44 + /**
  45 + *获取表单数据
  46 + */
37 47 function getFormData(){
38   - return [getFieldsValue()];
  48 + let formData = getFieldsValue();
  49 + Object.keys(formData).map(k=>{
  50 + if(formData[k] instanceof Array){
  51 + formData[k] = formData[k].join(',')
  52 + }
  53 + });
  54 + return [formData];
39 55 }
  56 +
40 57 /**
41 58 *表单校验
42 59 */
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -205,17 +205,14 @@ public class ${entityName}Controller {
205 205 QueryWrapper<${entityName}> queryWrapper = QueryGenerator.initQueryWrapper(${entityName?uncap_first}, request.getParameterMap());
206 206 LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
207 207  
208   - //Step.2 获取导出数据
209   - List<${entityName}> queryList = ${entityName?uncap_first}Service.list(queryWrapper);
210   - // 过滤选中数据
  208 + //配置选中数据查询条件
211 209 String selections = request.getParameter("selections");
212   - List<${entityName}> ${entityName?uncap_first}List = new ArrayList<${entityName}>();
213   - if(oConvertUtils.isEmpty(selections)) {
214   - ${entityName?uncap_first}List = queryList;
215   - }else {
216   - List<String> selectionList = Arrays.asList(selections.split(","));
217   - ${entityName?uncap_first}List = queryList.stream().filter(item -> selectionList.contains(item.getId())).collect(Collectors.toList());
  210 + if(oConvertUtils.isNotEmpty(selections)) {
  211 + List<String> selectionList = Arrays.asList(selections.split(","));
  212 + queryWrapper.in("id",selectionList);
218 213 }
  214 + //Step.2 获取导出数据
  215 + List<${entityName}> ${entityName?uncap_first}List = ${entityName?uncap_first}Service.list(queryWrapper);
219 216  
220 217 // Step.3 组装pageList
221 218 List<${entityName}Page> pageList = new ArrayList<${entityName}Page>();
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
  1 +<#include "/common/utils.ftl">
1 2 package ${bussiPackage}.${entityPackage}.entity;
2 3  
3 4 import java.io.Serializable;
... ... @@ -31,9 +32,9 @@ public class ${entityName} implements Serializable {
31 32 <#-- 生成字典Code -->
32 33 <#assign list_field_dictCode="">
33 34 <#if po.classType='sel_user'>
34   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  35 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
35 36 <#elseif po.classType='sel_depart'>
36   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  37 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
37 38 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
38 39 <#if po.dictTable?default("")?trim?length gt 1>
39 40 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/entity/[1-n]Entity.javai
  1 +<#include "/common/utils.ftl">
1 2 <#list subTables as subTab>
2 3 #segment#${subTab.entityName}.java
3 4 package ${bussiPackage}.${entityPackage}.entity;
... ... @@ -32,9 +33,9 @@ public class ${subTab.entityName} implements Serializable {
32 33 <#-- 生成字典Code -->
33 34 <#assign list_field_dictCode="">
34 35 <#if po.classType='sel_user'>
35   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  36 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
36 37 <#elseif po.classType='sel_depart'>
37   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  38 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
38 39 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
39 40 <#if po.dictTable?default("")?trim?length gt 1>
40 41 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Form.vuei
... ... @@ -48,7 +48,7 @@
48 48 @input="popupCallback"
49 49 <#if po.readonly=='Y'>disabled</#if>/>
50 50 <#elseif po.classType =='sel_depart'>
51   - <j-select-depart v-model="model.${po.fieldName}" multi <#if po.readonly=='Y'>disabled</#if>/>
  51 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
52 52 <#elseif po.classType =='switch'>
53 53 <j-switch v-model="model.${po.fieldName}" <#if po.dictField != 'is_open'>:options="${po.dictField}"</#if> <#if po.readonly=='Y'>disabled</#if>></j-switch>
54 54 <#elseif po.classType =='pca'>
... ... @@ -58,7 +58,7 @@
58 58 <#elseif po.classType =='password'>
59 59 <a-input-password v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
60 60 <#elseif po.classType =='sel_user'>
61   - <j-select-user-by-dep v-model="model.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if>/>
  61 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
62 62 <#elseif po.classType =='textarea'>
63 63 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
64 64 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/[1-n]Form.vuei
... ... @@ -44,7 +44,7 @@
44 44 :multi="${po.extendParams.popupMulti?c}"
45 45 @input="popupCallback"/>
46 46 <#elseif po.classType =='sel_depart'>
47   - <j-select-depart v-model="model.${po.fieldName}" multi/>
  47 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
48 48 <#elseif po.classType =='switch'>
49 49 <j-switch v-model="model.${po.fieldName}" <#if po.dictField!= 'is_open'>:options="${po.dictField}"</#if>></j-switch>
50 50 <#elseif po.classType =='pca'>
... ... @@ -54,7 +54,7 @@
54 54 <#elseif po.classType =='password'>
55 55 <a-input-password v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}"/>
56 56 <#elseif po.classType =='sel_user'>
57   - <j-select-user-by-dep v-model="model.${po.fieldName}"/>
  57 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
58 58 <#elseif po.classType =='textarea'>
59 59 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}"/>
60 60 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei
... ... @@ -46,6 +46,10 @@
46 46 <template #htmlSlot="{text}">
47 47 <div v-html="text"></div>
48 48 </template>
  49 + <!--省市区字段回显插槽-->
  50 + <template #pcaSlot="{text}">
  51 + {{ getAreaTextByCode(text) }}
  52 + </template>
49 53 <template #fileSlot="{text}">
50 54 <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
51 55 <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
... ... @@ -64,6 +68,10 @@
64 68 import ${entityName}Modal from './components/${entityName}Modal.vue'
65 69 import {columns, searchFormSchema} from './${entityName}.data';
66 70 import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName}.api';
  71 + import {downloadFile} from '/@/utils/common/renderUtils';
  72 +<#if list_need_pca>
  73 + import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
  74 +</#if>
67 75 <#if list_need_category>
68 76 import { loadCategoryData } from '/@/api/common/api'
69 77 import { getAuthCache, setAuthCache } from '/@/utils/auth';
... ... @@ -84,6 +92,17 @@
84 92 schemas: searchFormSchema,
85 93 autoSubmitOnEnter:true,
86 94 showAdvancedButton:true,
  95 + fieldMapToNumber: [
  96 + <#list columns as po>
  97 + <#if po.isQuery=='Y'>
  98 + <#if po.queryMode!='single'>
  99 + <#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  100 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end']],
  101 + </#if>
  102 + </#if>
  103 + </#if>
  104 + </#list>
  105 + ],
87 106 fieldMapToTime: [
88 107 <#list columns as po>
89 108 <#if po.isQuery=='Y'>
... ... @@ -98,9 +117,10 @@
98 117 </#list>
99 118 ],
100 119 },
101   - actionColumn: {
  120 + actionColumn: {
102 121 width: 120,
103   - },
  122 + fixed:'right'
  123 + },
104 124 },
105 125 exportConfig: {
106 126 name:"${tableVo.ftlDescription}",
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi
  1 +<#include "/common/utils.ftl">
1 2 import {BasicColumn} from '/@/components/Table';
2 3 import {FormSchema} from '/@/components/Table';
3 4 import { rules} from '/@/utils/helper/validator';
... ... @@ -25,7 +26,7 @@ export const columns: BasicColumn[] = [
25 26 slots: { customRender: 'htmlSlot' },
26 27 <#elseif po.classType=='pca'>
27 28 dataIndex: '${po.fieldName}',
28   - slots: { customRender: 'pcaSlot' },//TODO 未翻译
  29 + slots: { customRender: 'pcaSlot' },
29 30 <#elseif po.classType=='file'>
30 31 dataIndex: '${po.fieldName}',
31 32 slots: { customRender: 'fileSlot' },
... ... @@ -57,7 +58,7 @@ export const columns: BasicColumn[] = [
57 58 return render.renderCategoryTree(text,'${po.dictField?default("")}')
58 59 },
59 60 <#else>
60   - customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  61 + customRender: ({text, record}) => (text ? record['${po.dictText}'] : '')
61 62 </#if>
62 63 <#else>
63 64 dataIndex: '${po.fieldName}'
... ... @@ -95,16 +96,22 @@ export const searchFormSchema: FormSchema[] = [
95 96 <#elseif po.classType=='switch'>
96 97 component: 'JSwitch',
97 98 componentProps:{
  99 + query:true,
98 100 <#if po.dictField != 'is_open'>
99   - options:"${po.dictField}"
  101 + options:${po.dictField}
100 102 </#if>
101 103 },
102 104 <#elseif po.classType=='sel_depart'>
103 105 component: 'JSelectDept',
104 106 <#elseif po.classType=='list_multi'>
105   - component: 'JMultiSelectTag',//暂无该组件
  107 + component: 'JSelectMultiple',
106 108 componentProps:{
107   - dictCode:"query_field_dictCode?default("")"
  109 + <#if po.dictTable?default("")?trim?length gt 1>
  110 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}",
  111 + <#elseif po.dictField?default("")?trim?length gt 1>
  112 + dictCode:"${po.dictField}",
  113 + </#if>
  114 + triggerChange: true
108 115 },
109 116 <#elseif po.classType=='cat_tree'>
110 117 component: 'JCategorySelect',
... ... @@ -121,16 +128,7 @@ export const searchFormSchema: FormSchema[] = [
121 128 <#elseif po.classType=='pca'>
122 129 component: 'JAreaLinkage',
123 130 <#elseif po.classType=='popup'>
124   - component: 'JPopup',
125   - componentProps: ({ formActionType }) => {
126   - const {setFieldsValue} = formActionType;
127   - return{
128   - setFieldsValue:setFieldsValue,
129   - code:"${po.dictTable}",
130   - fieldConfig:"${po.dictField}",
131   - multi:${po.extendParams.popupMulti?c},
132   - }
133   - },
  131 + <#include "/common/form/vue3popup.ftl">
134 132 <#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
135 133 <#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
136 134 component: 'JDictSelectTag',
... ... @@ -157,6 +155,8 @@ export const searchFormSchema: FormSchema[] = [
157 155 componentProps: {
158 156 showTime:true
159 157 },
  158 +<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  159 + component: 'JRangeNumber',
160 160 <#else>
161 161 component: 'Input', //TODO 范围查询
162 162 </#if>
... ... @@ -189,27 +189,22 @@ export const formSchema: FormSchema[] = [
189 189 </#if>
190 190 {
191 191 label: '${po.filedComment}',
192   - field: '${po.fieldName}',
  192 + field: ${autoStringSuffix(po)},
193 193 <#if po.classType =='date'>
194 194 component: 'DatePicker',
195   - <#elseif po.fieldType =='datetime'>
  195 + <#elseif po.classType =='datetime'>
196 196 component: 'DatePicker',
197 197 componentProps: {
198   - showTime:true
  198 + showTime:true,
  199 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
199 200 },
200   - <#elseif po.fieldType =='time'>
  201 + <#elseif po.classType =='time'>
201 202 component: 'TimePicker',
  203 + componentProps: {
  204 + valueFormat: 'HH:mm:ss'
  205 + },
202 206 <#elseif po.classType =='popup'>
203   - component: 'JPopup',
204   - componentProps: ({ formActionType }) => {
205   - const {setFieldsValue} = formActionType;
206   - return{
207   - setFieldsValue:setFieldsValue,
208   - code:"${po.dictTable}",
209   - fieldConfig:${po.dictField},
210   - multi:${po.extendParams.popupMulti?c},
211   - }
212   - },
  207 + <#include "/common/form/vue3popup.ftl">
213 208 <#elseif po.classType =='sel_depart'>
214 209 component: 'JSelectDept',
215 210 <#elseif po.classType =='switch'>
... ... @@ -231,14 +226,14 @@ export const formSchema: FormSchema[] = [
231 226 labelKey:'realname',
232 227 },
233 228 <#elseif po.classType =='textarea'>
234   - component: 'InputTextArea',//TODO 注意string转换问题
  229 + component: 'InputTextArea',
235 230 <#elseif po.classType=='list' || po.classType=='radio'>
236 231 component: 'JDictSelectTag',
237 232 componentProps:{
238 233 dictCode:"${form_field_dictCode}"
239 234 },
240 235 <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
241   - component: 'JMultiSelectTag',//TODO 暂无该组件
  236 + component: 'JSelectMultiple',
242 237 componentProps:{
243 238 dictCode:"${form_field_dictCode}"
244 239 },
... ... @@ -273,7 +268,7 @@ export const formSchema: FormSchema[] = [
273 268 </#if>
274 269 },
275 270 <#elseif po.classType=='umeditor'>
276   - component: 'JCodeEditor', //TODO String后缀暂未添加
  271 + component: 'JEditor',
277 272 <#elseif po.classType == 'sel_tree'>
278 273 component: 'JTreeSelect',
279 274 componentProps:{
... ... @@ -305,16 +300,16 @@ export const formSchema: FormSchema[] = [
305 300 </#if>
306 301 <#-- 唯一校验 -->
307 302 <#if fieldValidType == 'only'>
308   - {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  303 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
309 304 <#-- 6到16位数字 -->
310 305 <#elseif fieldValidType == 'n6-16'>
311 306 { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
312 307 <#-- 6到16位任意字符 -->
313 308 <#elseif fieldValidType == '*6-16'>
314 309 { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
315   - <#-- 6到18位字符串 -->
  310 + <#-- 6到18位字 -->
316 311 <#elseif fieldValidType == 's6-18'>
317   - { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  312 + { pattern: /^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
318 313 <#-- 网址 -->
319 314 <#elseif fieldValidType == 'url'>
320 315 { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
... ... @@ -373,10 +368,14 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
373 368 <#assign form_cat_tree = false>
374 369 <#assign form_cat_back = "">
375 370 <#assign bpm_flag=false>
  371 +<#assign sub_id_exists=false>
376 372 <#list sub.colums as po><#rt/>
377 373 <#if po.fieldDbName=='bpm_status'>
378 374 <#assign bpm_flag=true>
379 375 </#if>
  376 +<#if po.fieldDbName=='id'>
  377 + <#assign sub_id_exists=true>
  378 +</#if>
380 379 <#if po.isShow =='Y'>
381 380 <#assign form_field_dictCode="">
382 381 <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
... ... @@ -386,27 +385,22 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
386 385 </#if>
387 386 {
388 387 label: '${po.filedComment}',
389   - field: '${po.fieldName}',
  388 + field: ${autoStringSuffix(po)},
390 389 <#if po.classType =='date'>
391 390 component: 'DatePicker',
392   - <#elseif po.fieldType =='datetime'>
  391 + <#elseif po.classType =='datetime'>
393 392 component: 'DatePicker',
394 393 componentProps: {
395   - showTime:true
  394 + showTime:true,
  395 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
396 396 },
397   - <#elseif po.fieldType =='time'>
  397 + <#elseif po.classType =='time'>
398 398 component: 'TimePicker',
  399 + componentProps: {
  400 + valueFormat: 'HH:mm:ss'
  401 + },
399 402 <#elseif po.classType =='popup'>
400   - component: 'JPopup',
401   - componentProps: ({ formActionType }) => {
402   - const {setFieldsValue} = formActionType;
403   - return{
404   - setFieldsValue:setFieldsValue,
405   - code:"${po.dictTable}",
406   - fieldConfig:${po.dictField},
407   - multi:${po.extendParams.popupMulti?c},
408   - }
409   - },
  403 + <#include "/common/form/vue3popup.ftl">
410 404 <#elseif po.classType =='sel_depart'>
411 405 component: 'JSelectDept',
412 406 <#elseif po.classType =='switch'>
... ... @@ -428,14 +422,14 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
428 422 labelKey:'realname',
429 423 },
430 424 <#elseif po.classType =='textarea'>
431   - component: 'InputTextArea',//TODO 注意string转换问题
  425 + component: 'InputTextArea',
432 426 <#elseif po.classType=='list' || po.classType=='radio'>
433 427 component: 'JDictSelectTag',
434 428 componentProps:{
435 429 dictCode:"${form_field_dictCode}"
436 430 },
437 431 <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
438   - component: 'JMultiSelectTag',//TODO 暂无该组件
  432 + component: 'JSelectMultiple',
439 433 componentProps:{
440 434 dictCode:"${form_field_dictCode}"
441 435 }
... ... @@ -470,7 +464,7 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
470 464 </#if>
471 465 },
472 466 <#elseif po.classType=='umeditor'>
473   - component: 'JCodeEditor', //TODO String后缀暂未添加
  467 + component: 'JEditor',
474 468 <#elseif po.classType == 'sel_tree'>
475 469 component: 'JTreeSelect',
476 470 componentProps:{
... ... @@ -502,16 +496,16 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
502 496 </#if>
503 497 <#-- 唯一校验 -->
504 498 <#if fieldValidType == 'only'>
505   - {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  499 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
506 500 <#-- 6到16位数字 -->
507 501 <#elseif fieldValidType == 'n6-16'>
508 502 { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
509 503 <#-- 6到16位任意字符 -->
510 504 <#elseif fieldValidType == '*6-16'>
511 505 { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
512   - <#-- 6到18位字符串 -->
  506 + <#-- 6到18位字 -->
513 507 <#elseif fieldValidType == 's6-18'>
514   - { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  508 + { pattern: /^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
515 509 <#-- 网址 -->
516 510 <#elseif fieldValidType == 'url'>
517 511 { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
... ... @@ -553,6 +547,14 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
553 547 },
554 548 </#if>
555 549 </#list>
  550 +<#if sub_id_exists == false>
  551 + {
  552 + label: '',
  553 + field: 'id',
  554 + component: 'Input',
  555 + show: false
  556 + },
  557 +</#if>
556 558 ];
557 559 </#if>
558 560 </#list>
... ... @@ -579,12 +581,20 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
579 581 <#if col.readonly=='Y'>
580 582 disabled:true,
581 583 </#if>
  584 +<#elseif col.classType =='time'>
  585 + type: JVxeTypes.time,
  586 + <#if col.readonly=='Y'>
  587 + disabled:true,
  588 + </#if>
582 589 <#elseif col.classType =='textarea'>
583 590 type: JVxeTypes.textarea,
584 591 <#if col.readonly=='Y'>
585 592 disabled:true,
586 593 </#if>
587   -<#elseif "int,decimal,double,"?contains(col.classType)>
  594 +<#-- update-begin-author:taoyan date:20220523 for: VUEN-1084 【vue3】online表单测试发现的新问题 20、一对多列字段类型生成的不对,数字或者金额类型 -->
  595 +<#-- elseif "int,decimal,double,"?contains(col.classType) -->
  596 +<#elseif col.fieldDbType=='int' || col.fieldDbType=='double' || col.fieldDbType=='BigDecimal'>
  597 +<#-- update-end-author:taoyan date:20220523 for: VUEN-1084 【vue3】online表单测试发现的新问题 20、一对多列字段类型生成的不对,数字或者金额类型 -->
588 598 type: JVxeTypes.inputNumber,
589 599 <#if col.readonly=='Y'>
590 600 disabled:true,
... ... @@ -621,6 +631,16 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
621 631 <#if col.readonly=='Y'>
622 632 disabled:true,
623 633 </#if>
  634 +<#elseif col.classType =='sel_depart'>
  635 + type: JVxeTypes.departSelect,
  636 + <#if col.readonly=='Y'>
  637 + disabled:true,
  638 + </#if>
  639 +<#elseif col.classType =='sel_user'>
  640 + type: JVxeTypes.userSelect,
  641 + <#if col.readonly=='Y'>
  642 + disabled:true,
  643 + </#if>
624 644 <#elseif col.classType =='image'>
625 645 type: JVxeTypes.image,
626 646 token:true,
... ... @@ -644,9 +664,9 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
644 664 <#elseif col.classType =='switch'>
645 665 type: JVxeTypes.checkbox,
646 666 <#if col.dictField == 'is_open'>
647   - customValue: ['Y', 'N'],
  667 + customValue: ['Y', 'N'],
648 668 <#else>
649   - customValue: ${col.dictField},
  669 + customValue: ${col.dictField},
650 670 </#if>
651 671 <#if col.readonly=='Y'>
652 672 disabled:true,
... ... @@ -657,18 +677,11 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
657 677 <#else>
658 678 <#assign popupBackFields = "${col.dictText}">
659 679 </#if>
660   - type: JVxeTypes.popup,
661   - popupCode:"${col.dictTable}",
662   - field:"${col.dictField}",
663   - orgFields:"${col.dictField}",
664   - destFields:"${Format.underlineToHump(col.dictText)}",
665   - <#if col.readonly=='Y'>
666   - disabled:true,
667   - </#if>
  680 + <#include "/common/form/vue3Jvxepopup.ftl">
668 681 <#else>
669   - type: JVxeTypes.input,
  682 + type: JVxeTypes.input,
670 683 <#if col.readonly=='Y'>
671   - disabled:true,
  684 + disabled:true,
672 685 </#if>
673 686 </#if>
674 687 <#if col.classType =='list_multi' || col.classType =='checkbox'>
... ... @@ -691,18 +704,7 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
691 704 defaultValue:'',
692 705 </#if>
693 706 <#-- 子表的校验 -->
694   -<#assign subFieldValidType = col.fieldValidType!''>
695   -<#-- 非空校验 -->
696   -<#if col.nullable == 'N' || subFieldValidType == '*'>
697   - validateRules: [{ required: true, message: '${'$'}{title}不能为空' }],
698   -<#-- 其他情况下,只要有值就被认为是正则校验 -->
699   -<#elseif subFieldValidType?length gt 0>
700   -<#assign subMessage = '格式不正确'>
701   -<#if subFieldValidType == 'only' >
702   - <#assign subMessage = '不能重复'>
703   -</#if>
704   - validateRules: [{ pattern: "${subFieldValidType}", message: "${'$'}{title}${subMessage}" }],
705   -</#if>
  707 + <#include "/common/validatorRulesTemplate/sub-vue3.ftl">
706 708 },
707 709 </#if>
708 710 </#if>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei
  1 +<#include "/common/utils.ftl">
1 2 <template>
2   - <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  3 + <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="${getModalWidth(tableVo.fieldRowNum?default(1))}" @ok="handleSubmit">
3 4 <BasicForm @register="registerForm" ref="formRef"/>
4 5 <!-- 子表单区域 -->
5 6 <a-tabs v-model:activeKey="activeKey" @change="handleChangeTabs">
... ... @@ -7,7 +8,7 @@
7 8 <#assign refKey = sub.entityName?uncap_first/>
8 9 <#if sub.foreignRelationType =='1'>
9 10 <a-tab-pane tab="${sub.ftlDescription}" key="${refKey}" :forceRender="true">
10   - <${sub.entityName}Form ref="${sub.entityName?uncap_first}Form"></${sub.entityName}Form>
  11 + <${sub.entityName}Form ref="${sub.entityName?uncap_first}Form" :disabled="formDisabled"></${sub.entityName}Form>
11 12 </a-tab-pane>
12 13  
13 14 <#else>
... ... @@ -19,9 +20,10 @@
19 20 :loading="${sub.entityName?uncap_first}Table.loading"
20 21 :columns="${sub.entityName?uncap_first}Table.columns"
21 22 :dataSource="${sub.entityName?uncap_first}Table.dataSource"
22   - :maxHeight="300"
  23 + :height="340"
23 24 :rowNumber="true"
24 25 :rowSelection="true"
  26 + :disabled="formDisabled"
25 27 :toolbar="true"
26 28 />
27 29 </a-tab-pane>
... ... @@ -48,6 +50,7 @@
48 50 // Emits声明
49 51 const emit = defineEmits(['register','success']);
50 52 const isUpdate = ref(true);
  53 + const formDisabled = ref(false);
51 54 const refKeys = ref([<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
52 55 <#assign hasOne2Many = false>
53 56 <#assign hasOne2One = false>
... ... @@ -77,6 +80,7 @@
77 80 labelWidth: 150,
78 81 schemas: formSchema,
79 82 showActionButtonGroup: false,
  83 + baseColProps: {span: ${getFormSpan(tableVo.fieldRowNum?default(1))}}
80 84 });
81 85 //表单赋值
82 86 const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
... ... @@ -84,6 +88,7 @@
84 88 await reset();
85 89 setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
86 90 isUpdate.value = !!data?.isUpdate;
  91 + formDisabled.value = !data?.showFooter;
87 92 if (unref(isUpdate)) {
88 93 //表单赋值
89 94 await setFieldsValue({
... ... @@ -176,5 +181,12 @@
176 181 </script>
177 182  
178 183 <style lang="less" scoped>
  184 + /** 时间和数字输入框样式 */
  185 + :deep(.ant-input-number){
  186 + width: 100%
  187 + }
179 188  
  189 + :deep(.ant-calendar-picker){
  190 + width: 100%
  191 + }
180 192 </style>
181 193 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/[1-n]Form.vuei
  1 +<#include "/common/utils.ftl">
1 2 <#list subTables as sub>
2 3 <#if sub.foreignRelationType=='1'>
3 4 #segment#${sub.entityName}Form.vue
... ... @@ -15,11 +16,18 @@
15 16 name:"${sub.entityName}Form",
16 17 components: {BasicForm},
17 18 emits:['register'],
18   - setup(_,{emit}) {
19   - const [registerForm, {resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
  19 + props:{
  20 + disabled: {
  21 + type: Boolean,
  22 + default: false
  23 + }
  24 + },
  25 + setup(props,{emit}) {
  26 + const [registerForm, {setProps, resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
20 27 labelWidth: 150,
21 28 schemas: ${sub.entityName?uncap_first}FormSchema,
22 29 showActionButtonGroup: false,
  30 + baseColProps: {span: ${getFormSpan(tableVo.fieldRowNum?default(1))}}
23 31 });
24 32 /**
25 33 *初始化加载数据
... ... @@ -30,12 +38,19 @@
30 38 res.success && setFieldsValue({...res.result[0]});
31 39 })
32 40 }
  41 + setProps({disabled: props.disabled})
33 42 }
34 43 /**
35 44 *获取表单数据
36 45 */
37 46 function getFormData(){
38   - return [getFieldsValue()];
  47 + let formData = getFieldsValue();
  48 + Object.keys(formData).map(k=>{
  49 + if(formData[k] instanceof Array){
  50 + formData[k] = formData[k].join(',')
  51 + }
  52 + });
  53 + return [formData];
39 54 }
40 55 /**
41 56 *表单校验
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 + <#assign query_field_no=0>
  4 + <#assign need_category = false>
  5 + <#assign need_pca = false>
  6 + <#assign need_search = false>
  7 + <#assign need_dept_user = false>
  8 + <#assign need_switch = false>
  9 + <#assign need_dept = false>
  10 + <#assign need_multi = false>
  11 + <#assign need_popup = false>
  12 + <#assign need_select_tag = false>
  13 + <#assign need_select_tree = false>
  14 + <#assign need_time = false>
  15 + <#assign bpm_flag=false>
  16 + <#assign need_markdown = false>
  17 + <#assign need_upload = false>
  18 + <#assign need_image_upload = false>
  19 + <#assign need_editor = false>
  20 + <#assign need_checkbox = false>
  21 + <!--查询区域-->
  22 + <div class="jeecg-basic-table-form-container">
  23 + <a-form @keyup.enter.native="reload" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
  24 + <a-row :gutter="24">
  25 + <#-- 开始循环 -->
  26 + <#list columns as po>
  27 + <#if po.fieldDbName=='bpm_status'>
  28 + <#assign bpm_flag=true>
  29 + </#if>
  30 + <#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
  31 + <#assign need_category=true>
  32 + </#if>
  33 + <#if po.classType=='pca'>
  34 + <#assign need_pca=true>
  35 + </#if>
  36 + <#if po.classType=='sel_search'>
  37 + <#assign need_search = true>
  38 + </#if>
  39 + <#if po.classType=='sel_user'>
  40 + <#assign need_dept_user = true>
  41 + </#if>
  42 + <#if po.classType=='sel_depart'>
  43 + <#assign need_dept = true>
  44 + </#if>
  45 + <#if po.classType=='switch'>
  46 + <#assign need_switch = true>
  47 + </#if>
  48 + <#if po.classType=='list_multi'>
  49 + <#assign need_multi = true>
  50 + </#if>
  51 + <#if po.classType=='popup'>
  52 + <#assign need_popup = true>
  53 + </#if>
  54 + <#if po.classType=='sel_tree'>
  55 + <#assign need_select_tree = true>
  56 + </#if>
  57 + <#if po.classType=='time'>
  58 + <#assign need_time = true>
  59 + </#if>
  60 + <#include "/common/form/native/vue3NativeSearch.ftl">
  61 + </#list>
  62 + <#if query_field_no gt 2>
  63 + </template>
  64 +</#if>
  65 +<#if query_flag>
  66 + <a-col :xl="6" :lg="7" :md="8" :sm="24">
  67 + <span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
  68 + <a-col :lg="6">
  69 + <a-button type="primary" preIcon="ant-design:search-outlined" @click="reload">查询</a-button>
  70 + <a-button preIcon="ant-design:reload-outlined" @click="searchReset" style="margin-left: 8px">重置</a-button>
  71 + <a @click="toggleSearchStatus = !toggleSearchStatus" style="margin-left: 8px">
  72 + {{ toggleSearchStatus ? '收起' : '展开' }}
  73 + <Icon :icon="toggleSearchStatus ? 'ant-design:up-outlined' : 'ant-design:down-outlined'" />
  74 + </a>
  75 + </a-col>
  76 + </span>
  77 + </a-col>
  78 +</#if>
  79 + </a-row>
  80 + </a-form>
  81 + </div>
  82 +<#-- 结束循环 -->
  83 + <!--引用表格-->
  84 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  85 + <!--插槽:table标题-->
  86 + <template #tableTitle>
  87 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  88 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  89 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  90 + <a-dropdown v-if="selectedRowKeys.length > 0">
  91 + <template #overlay>
  92 + <a-menu>
  93 + <a-menu-item key="1" @click="batchHandleDelete">
  94 + <Icon icon="ant-design:delete-outlined"></Icon>
  95 + 删除
  96 + </a-menu-item>
  97 + </a-menu>
  98 + </template>
  99 + <a-button>批量操作
  100 + <Icon icon="mdi:chevron-down"></Icon>
  101 + </a-button>
  102 + </a-dropdown>
  103 + </template>
  104 + <!--操作栏-->
  105 + <template #action="{ record }">
  106 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  107 + </template>
  108 + <!--字段回显插槽-->
  109 + <template #htmlSlot="{text}">
  110 + <div v-html="text"></div>
  111 + </template>
  112 + <!--省市区字段回显插槽-->
  113 + <template #pcaSlot="{text}">
  114 + {{ getAreaTextByCode(text) }}
  115 + </template>
  116 + <template #fileSlot="{text}">
  117 + <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
  118 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  119 + </template>
  120 + </BasicTable>
  121 + <!-- 表单区域 -->
  122 + <${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
  123 + </div>
  124 +</template>
  125 +
  126 +<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
  127 + import {ref, reactive, computed, unref} from 'vue';
  128 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  129 + import { useListPage } from '/@/hooks/system/useListPage'
  130 + import {useModal} from '/@/components/Modal';
  131 + import ${entityName}Modal from './components/${entityName}Modal.vue'
  132 + import {columns, searchFormSchema} from './${entityName}.data';
  133 + import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName}.api';
  134 + import {downloadFile} from '/@/utils/common/renderUtils';
  135 + <#include "/common/form/native/vue3NativeImport.ftl">
  136 +<#if need_pca>
  137 + import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
  138 +</#if>
  139 + <#if need_category>
  140 + import { loadCategoryData } from '/@/api/common/api'
  141 + import { getAuthCache, setAuthCache } from '/@/utils/auth';
  142 + import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
  143 + </#if>
  144 + const checkedKeys = ref<Array<string | number>>([]);
  145 + //注册model
  146 + const [registerModal, {openModal}] = useModal();
  147 + //注册table数据
  148 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  149 + tableProps:{
  150 + title: '${tableVo.ftlDescription}',
  151 + api: list,
  152 + columns,
  153 + canResize:false,
  154 + useSearchForm: false,
  155 + actionColumn: {
  156 + width: 120,
  157 + fixed:'right'
  158 + },
  159 + beforeFetch: (params) => {
  160 + return Object.assign(params, queryParam.value);
  161 + },
  162 + },
  163 + exportConfig: {
  164 + name:"${tableVo.ftlDescription}",
  165 + url: getExportUrl,
  166 + },
  167 + importConfig: {
  168 + url: getImportUrl,
  169 + success: handleSuccess
  170 + },
  171 + })
  172 +
  173 + const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
  174 +
  175 + /**
  176 + * 新增事件
  177 + */
  178 + function handleAdd() {
  179 + openModal(true, {
  180 + isUpdate: false,
  181 + showFooter: true,
  182 + });
  183 + }
  184 + /**
  185 + * 编辑事件
  186 + */
  187 + function handleEdit(record: Recordable) {
  188 + openModal(true, {
  189 + record,
  190 + isUpdate: true,
  191 + showFooter: true,
  192 + });
  193 + }
  194 + /**
  195 + * 详情
  196 + */
  197 + function handleDetail(record: Recordable) {
  198 + openModal(true, {
  199 + record,
  200 + isUpdate: true,
  201 + showFooter: false,
  202 + });
  203 + }
  204 + /**
  205 + * 删除事件
  206 + */
  207 + async function handleDelete(record) {
  208 + await deleteOne({id: record.id}, handleSuccess);
  209 + }
  210 + /**
  211 + * 批量删除事件
  212 + */
  213 + async function batchHandleDelete() {
  214 + await batchDelete({ids: selectedRowKeys.value},handleSuccess);
  215 + }
  216 + /**
  217 + * 成功回调
  218 + */
  219 + function handleSuccess() {
  220 + (selectedRowKeys.value = []) && reload();
  221 + }
  222 + /**
  223 + * 操作栏
  224 + */
  225 + function getTableAction(record){
  226 + return [
  227 + {
  228 + label: '编辑',
  229 + onClick: handleEdit.bind(null, record),
  230 + }
  231 + ]
  232 + }
  233 + /**
  234 + * 下拉操作栏
  235 + */
  236 + function getDropDownAction(record){
  237 + return [
  238 + {
  239 + label: '详情',
  240 + onClick: handleDetail.bind(null, record),
  241 + }, {
  242 + label: '删除',
  243 + popConfirm: {
  244 + title: '是否确认删除',
  245 + confirm: handleDelete.bind(null, record),
  246 + }
  247 + }
  248 + ]
  249 + }
  250 + <#if need_category>
  251 + /**
  252 + * form点击事件
  253 + * @param value
  254 + */
  255 + function handleFormChange(key, value) {
  256 + queryParam.value[key] = value;
  257 + }
  258 + /**
  259 + * 初始化字典配置
  260 + */
  261 + function initDictConfig(){
  262 + <#list columns as po>
  263 + <#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
  264 + <#if po.classType=='cat_tree' && need_category==true>
  265 + loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
  266 + if (res) {
  267 + let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
  268 + if(!allDictDate['${po.dictField?default("")}']){
  269 + Object.assign(allDictDate,{'${po.dictField?default("")}':res})
  270 + }
  271 + setAuthCache(DB_DICT_DATA_KEY,allDictDate)
  272 + }
  273 + })
  274 + </#if>
  275 + </#if>
  276 + </#list>
  277 + }
  278 + initDictConfig();
  279 + </#if>
  280 +
  281 + /* ----------------------以下为原生查询需要添加的-------------------------- */
  282 + const queryParam = ref<any>({});
  283 + const toggleSearchStatus = ref<boolean>(false);
  284 + const labelCol = reactive({
  285 + xs: { span: 24 },
  286 + sm: { span: 7 },
  287 + });
  288 + const wrapperCol = reactive({
  289 + xs: { span: 24 },
  290 + sm: { span: 16 },
  291 + });
  292 + /**
  293 + * 重置
  294 + */
  295 + function searchReset() {
  296 + queryParam.value = {};
  297 + selectedRowKeys.value = [];
  298 + //刷新数据
  299 + reload();
  300 + }
  301 + <#if need_popup>
  302 + /**
  303 + * popup组件值改变事件
  304 + */
  305 + function setFieldsValue(map) {
  306 + Object.keys(map).map((key) => {
  307 + queryParam.value[key] = map[key];
  308 + });
  309 + }
  310 + </#if>
  311 +
  312 + <#if need_pca>
  313 + /**
  314 + * 省市区点击事件
  315 + * @param key
  316 + * @param value
  317 + */
  318 + function handleAreaChange(key, value) {
  319 + queryParam.value[key] = value.join(',');
  320 + }
  321 + </#if>
  322 +</script>
  323 +<style lang="less" scoped>
  324 + .jeecg-basic-table-form-container {
  325 + .table-page-search-submitButtons {
  326 + display: block;
  327 + margin-bottom: 24px;
  328 + white-space: nowrap;
  329 + }
  330 + .query-group-cust{
  331 + width: calc(50% - 15px);
  332 + min-width: 100px !important;
  333 + }
  334 + .query-group-split-cust{
  335 + width: 30px;
  336 + display: inline-block;
  337 + text-align: center
  338 + }
  339 + }
  340 +</style>
0 341 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from '/@/utils/http/axios';
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 + queryDataById = '/${entityPackage}/${entityName?uncap_first}/queryById',
  13 +<#list subTables as sub><#rt/>
  14 + ${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/query${sub.entityName}ByMainId',
  15 +</#list>
  16 +}
  17 +/**
  18 + * 导出api
  19 + * @param params
  20 + */
  21 +export const getExportUrl = Api.exportXls;
  22 +
  23 +/**
  24 + * 导入api
  25 + */
  26 +export const getImportUrl = Api.importExcel;
  27 +
  28 +<#list subTables as sub><#rt/>
  29 +/**
  30 + * 查询子表数据
  31 + * @param params
  32 + */
  33 +export const query${sub.entityName}ListByMainId = (id) => defHttp.get({url: Api.${sub.entityName?uncap_first}List, params:{ id }});
  34 +</#list>
  35 +
  36 +/**
  37 + * 列表接口
  38 + * @param params
  39 + */
  40 +export const list = (params) =>
  41 + defHttp.get({url: Api.list, params});
  42 +
  43 +/**
  44 + * 删除单个
  45 + */
  46 +export const deleteOne = (params,handleSuccess) => {
  47 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  48 + handleSuccess();
  49 + });
  50 +}
  51 +/**
  52 + * 批量删除
  53 + * @param params
  54 + */
  55 +export const batchDelete = (params, handleSuccess) => {
  56 + Modal.confirm({
  57 + title: '确认删除',
  58 + content: '是否删除选中数据',
  59 + okText: '确认',
  60 + cancelText: '取消',
  61 + onOk: () => {
  62 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  63 + handleSuccess();
  64 + });
  65 + }
  66 + });
  67 +}
  68 +/**
  69 + * 保存或者更新
  70 + * @param params
  71 + */
  72 +export const saveOrUpdate = (params, isUpdate) => {
  73 + let url = isUpdate ? Api.edit : Api.save;
  74 + return defHttp.post({url: url, params});
  75 +}
  76 +
  77 +/**
  78 +* 根据id查询数据
  79 +* @param params
  80 +*/
  81 +export const queryDataById = (id) => defHttp.get({url: Api.queryDataById, params:{ id }});
  82 +
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}__data.tsi 0 → 100644
  1 +<#include "/common/utils.ftl">
  2 +import {BasicColumn} from '/@/components/Table';
  3 +import {FormSchema} from '/@/components/Table';
  4 +import { rules} from '/@/utils/helper/validator';
  5 +import { render } from '/@/utils/common/renderUtils';
  6 +import {JVxeTypes,JVxeColumn} from '/@/components/jeecg/JVxeTable/types'
  7 +//列表数据
  8 +export const columns: BasicColumn[] = [
  9 + <#list columns as po>
  10 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  11 + {
  12 + title: '${po.filedComment}',
  13 + align:"center",
  14 + <#if po.sort=='Y'>
  15 + sorter: true,
  16 + </#if>
  17 + <#if po.classType=='date'>
  18 + dataIndex: '${po.fieldName}',
  19 + customRender:({text}) =>{
  20 + return !text?"":(text.length>10?text.substr(0,10):text)
  21 + },
  22 + <#elseif po.fieldDbType=='Blob'>
  23 + dataIndex: '${po.fieldName}String'
  24 + <#elseif po.classType=='umeditor'>
  25 + dataIndex: '${po.fieldName}',
  26 + slots: { customRender: 'htmlSlot' },
  27 + <#elseif po.classType=='pca'>
  28 + dataIndex: '${po.fieldName}',
  29 + slots: { customRender: 'pcaSlot' },
  30 + <#elseif po.classType=='file'>
  31 + dataIndex: '${po.fieldName}',
  32 + slots: { customRender: 'fileSlot' },
  33 + <#elseif po.classType=='image'>
  34 + dataIndex: '${po.fieldName}',
  35 + customRender:render.renderImage,
  36 + <#elseif po.classType=='switch'>
  37 + dataIndex: '${po.fieldName}',
  38 +<#assign switch_extend_arr=['Y','N']>
  39 +<#if po.dictField?default("")?contains("[")>
  40 +<#assign switch_extend_arr=po.dictField?eval>
  41 +</#if>
  42 +<#list switch_extend_arr as a>
  43 +<#if a_index == 0>
  44 +<#assign switch_extend_arr1=a>
  45 +<#else>
  46 +<#assign switch_extend_arr2=a>
  47 +</#if>
  48 +</#list>
  49 + customRender:({text}) => {
  50 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
  51 + },
  52 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  53 + dataIndex: '${po.fieldName}_dictText'
  54 + <#elseif po.classType=='cat_tree'>
  55 + dataIndex: '${po.fieldName}',
  56 + <#if po.dictText?default("")?trim?length == 0>
  57 + customRender:({text}) => {
  58 + return render.renderCategoryTree(text,'${po.dictField?default("")}')
  59 + },
  60 + <#else>
  61 + customRender: ({text, record}) => (text ? record['${po.dictText}'] : '')
  62 + </#if>
  63 + <#else>
  64 + dataIndex: '${po.fieldName}'
  65 + </#if>
  66 + },
  67 + </#if>
  68 + </#list>
  69 +];
  70 +//查询数据
  71 +export const searchFormSchema: FormSchema[] = [
  72 +<#-- 开始循环 -->
  73 +<#list columns as po>
  74 +<#if po.fieldDbName=='bpm_status'>
  75 + <#assign bpm_flag=true>
  76 +</#if>
  77 +<#if po.isQuery=='Y'>
  78 +<#assign query_flag=true>
  79 + <#assign query_field_dictCode="">
  80 + <#if po.dictTable?default("")?trim?length gt 1>
  81 + <#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  82 + <#elseif po.dictField?default("")?trim?length gt 1>
  83 + <#assign query_field_dictCode="${po.dictField}">
  84 + </#if>
  85 +<#if po.queryMode=='single'>
  86 + {
  87 + label: "${po.filedComment}",
  88 + field: "${po.fieldName}",
  89 +<#if po.classType=='sel_search'>
  90 + component: 'JSearchSelect',
  91 + componentProps:{
  92 + dict:"${po.dictTable},${po.dictText},${po.dictField}"
  93 + },
  94 +<#elseif po.classType=='sel_user'>
  95 + component: 'JSelectUserByDept',
  96 +<#elseif po.classType=='switch'>
  97 + component: 'JSwitch',
  98 + componentProps:{
  99 + <#if po.dictField != 'is_open'>
  100 + options:"${po.dictField}"
  101 + </#if>
  102 + },
  103 + <#elseif po.classType=='sel_depart'>
  104 + component: 'JSelectDept',
  105 + <#elseif po.classType=='list_multi'>
  106 + component: 'JSelectMultiple',
  107 + componentProps:{
  108 + <#if po.dictTable?default("")?trim?length gt 1>
  109 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}",
  110 + <#elseif po.dictField?default("")?trim?length gt 1>
  111 + dictCode:"${po.dictField}",
  112 + </#if>
  113 + triggerChange: true
  114 + },
  115 + <#elseif po.classType=='cat_tree'>
  116 + component: 'JCategorySelect',
  117 + componentProps:{
  118 + pcode:"${po.dictField?default("")}",//back和事件未添加,暂时有问题
  119 + },
  120 +<#elseif po.classType=='date'>
  121 + component: 'DatePicker',
  122 +<#elseif po.classType=='datetime'>
  123 + component: 'DatePicker',
  124 + componentProps: {
  125 + showTime:true
  126 + },
  127 +<#elseif po.classType=='pca'>
  128 + component: 'JAreaLinkage',
  129 +<#elseif po.classType=='popup'>
  130 + <#include "/common/form/vue3popup.ftl">
  131 +<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  132 +<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
  133 + component: 'JDictSelectTag',
  134 + componentProps:{
  135 + <#if po.dictTable?default("")?trim?length gt 1>
  136 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
  137 + <#elseif po.dictField?default("")?trim?length gt 1>
  138 + dictCode:"${po.dictField}"
  139 + </#if>
  140 + },
  141 +<#else>
  142 + component: 'Input',
  143 +</#if>
  144 + colProps: {span: 6},
  145 + },
  146 +<#else>
  147 + {
  148 + label: "${po.filedComment}",
  149 + field: "${po.fieldName}",
  150 +<#if po.classType=='date'>
  151 + component: 'RangePicker',
  152 +<#elseif po.classType=='datetime'>
  153 + component: 'RangePicker',
  154 + componentProps: {
  155 + showTime:true
  156 + },
  157 +<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  158 + component: 'JRangeNumber',
  159 +<#else>
  160 + component: 'Input', //TODO 范围查询
  161 +</#if>
  162 + colProps: {span: 6},
  163 + },
  164 +</#if>
  165 +</#if>
  166 +</#list>
  167 +<#-- 结束循环 -->
  168 +];
  169 +//表单数据
  170 +export const formSchema: FormSchema[] = [
  171 +<#assign form_cat_tree = false>
  172 +<#assign form_cat_back = "">
  173 +<#assign bpm_flag=false>
  174 +<#assign id_exists = false>
  175 +<#list columns as po><#rt/>
  176 +<#if po.fieldDbName=='bpm_status'>
  177 + <#assign bpm_flag=true>
  178 +</#if>
  179 +<#if po.fieldDbName == 'id'>
  180 + <#assign id_exists = true>
  181 +</#if>
  182 +<#if po.isShow =='Y'>
  183 +<#assign form_field_dictCode="">
  184 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  185 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  186 + <#elseif po.dictField?default("")?trim?length gt 1>
  187 + <#assign form_field_dictCode="${po.dictField}">
  188 + </#if>
  189 + {
  190 + label: '${po.filedComment}',
  191 + field: ${autoStringSuffix(po)},
  192 + <#if po.classType =='date'>
  193 + component: 'DatePicker',
  194 + <#elseif po.classType =='datetime'>
  195 + component: 'DatePicker',
  196 + componentProps: {
  197 + showTime:true,
  198 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
  199 + },
  200 + <#elseif po.classType =='time'>
  201 + component: 'TimePicker',
  202 + componentProps: {
  203 + valueFormat: 'HH:mm:ss'
  204 + },
  205 + <#elseif po.classType =='popup'>
  206 + <#include "/common/form/vue3popup.ftl">
  207 + <#elseif po.classType =='sel_depart'>
  208 + component: 'JSelectDept',
  209 + <#elseif po.classType =='switch'>
  210 + component: 'JSwitch',
  211 + componentProps:{
  212 + <#if po.dictField != 'is_open'>
  213 + options:${po.dictField}
  214 + </#if>
  215 + },
  216 + <#elseif po.classType =='pca'>
  217 + component: 'JAreaLinkage',
  218 + <#elseif po.classType =='markdown'>
  219 + component: 'JMarkdownEditor',//注意string转换问题
  220 + <#elseif po.classType =='password'>
  221 + component: 'InputPassword',
  222 + <#elseif po.classType =='sel_user'>
  223 + component: 'JSelectUserByDept',
  224 + componentProps:{
  225 + labelKey:'realname',
  226 + },
  227 + <#elseif po.classType =='textarea'>
  228 + component: 'InputTextArea',
  229 + <#elseif po.classType=='list' || po.classType=='radio'>
  230 + component: 'JDictSelectTag',
  231 + componentProps:{
  232 + dictCode:"${form_field_dictCode}"
  233 + },
  234 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  235 + component: 'JSelectMultiple',
  236 + componentProps:{
  237 + dictCode:"${form_field_dictCode}"
  238 + },
  239 + <#elseif po.classType=='sel_search'>
  240 + component: 'JSearchSelect',
  241 + componentProps:{
  242 + dict:"${form_field_dictCode}"
  243 + },
  244 +<#elseif po.classType=='cat_tree'>
  245 + <#assign form_cat_tree = true>
  246 + component: 'JCategorySelect',
  247 + componentProps:{
  248 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  249 + },
  250 + <#if po.dictText?default("")?trim?length gt 1>
  251 + <#assign form_cat_back = "${po.dictText}">
  252 + </#if>
  253 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  254 + component: 'InputNumber',
  255 + <#elseif po.classType=='file'>
  256 + component: 'JUpload',
  257 + componentProps:{
  258 + <#if po.uploadnum??>
  259 + maxCount:${po.uploadnum}
  260 + </#if>
  261 + },
  262 + <#elseif po.classType=='image'>
  263 + component: 'JImageUpload',
  264 + componentProps:{
  265 + <#if po.uploadnum??>
  266 + fileMax:${po.uploadnum}
  267 + </#if>
  268 + },
  269 + <#elseif po.classType=='umeditor'>
  270 + component: 'JEditor',
  271 + <#elseif po.classType == 'sel_tree'>
  272 + component: 'JTreeSelect',
  273 + componentProps:{
  274 + <#if po.dictText??>
  275 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  276 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  277 + <#elseif po.dictText?split(',')[1]??>
  278 + pidField:"${po.dictText?split(',')[1]}",
  279 + <#elseif po.dictText?split(',')[3]??>
  280 + hasChildField:"${po.dictText?split(',')[3]}",
  281 + </#if>
  282 + </#if>
  283 + pidValue:"${po.dictField}",
  284 + },
  285 + <#else>
  286 + component: 'Input',
  287 + </#if>
  288 + <#include "/common/utils.ftl">
  289 + <#if po.isShow == 'Y' && poHasCheck(po)>
  290 + dynamicRules: ({model,schema}) => {
  291 + <#if po.fieldName != 'id'>
  292 + <#assign fieldValidType = po.fieldValidType!''>
  293 + return [
  294 + <#-- 非空校验 -->
  295 + <#if po.nullable == 'N' || fieldValidType == '*'>
  296 + { required: true, message: '请输入${po.filedComment}!'},
  297 + <#elseif fieldValidType!=''>
  298 + { required: false},
  299 + </#if>
  300 + <#-- 唯一校验 -->
  301 + <#if fieldValidType == 'only'>
  302 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
  303 + <#-- 6到16位数字 -->
  304 + <#elseif fieldValidType == 'n6-16'>
  305 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  306 + <#-- 6到16位任意字符 -->
  307 + <#elseif fieldValidType == '*6-16'>
  308 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  309 + <#-- 6到18位字符串 -->
  310 + <#elseif fieldValidType == 's6-18'>
  311 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  312 + <#-- 网址 -->
  313 + <#elseif fieldValidType == 'url'>
  314 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  315 + <#-- 电子邮件 -->
  316 + <#elseif fieldValidType == 'e'>
  317 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  318 + <#-- 手机号码 -->
  319 + <#elseif fieldValidType == 'm'>
  320 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  321 + <#-- 邮政编码 -->
  322 + <#elseif fieldValidType == 'p'>
  323 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  324 + <#-- 字母 -->
  325 + <#elseif fieldValidType == 's'>
  326 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  327 + <#-- 数字 -->
  328 + <#elseif fieldValidType == 'n'>
  329 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  330 + <#-- 整数 -->
  331 + <#elseif fieldValidType == 'z'>
  332 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  333 + <#-- 金额 -->
  334 + <#elseif fieldValidType == 'money'>
  335 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  336 + <#-- 正则校验 -->
  337 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  338 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  339 + <#-- 无校验 -->
  340 + <#else>
  341 + <#t>
  342 + </#if>
  343 + ];
  344 + </#if>
  345 + },
  346 + </#if>
  347 + <#if po.readonly=='Y'>
  348 + dynamicDisabled:true
  349 + </#if>
  350 + },
  351 +</#if>
  352 +</#list>
  353 +<#if id_exists == false>
  354 + // TODO 主键隐藏字段,目前写死为ID
  355 + {
  356 + label: '',
  357 + field: 'id',
  358 + component: 'Input',
  359 + show: false
  360 + },
  361 +</#if>
  362 +];
  363 +//子表单数据
  364 +<#list subTables as sub>
  365 +<#if sub.foreignRelationType =='1'>
  366 +export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
  367 +<#assign form_cat_tree = false>
  368 +<#assign form_cat_back = "">
  369 +<#assign bpm_flag=false>
  370 +<#assign sub_id_exists=false>
  371 +<#list sub.colums as po><#rt/>
  372 +<#if po.fieldDbName=='bpm_status'>
  373 + <#assign bpm_flag=true>
  374 +</#if>
  375 +<#if po.fieldDbName=='id'>
  376 + <#assign sub_id_exists=true>
  377 +</#if>
  378 +<#if po.isShow =='Y'>
  379 +<#assign form_field_dictCode="">
  380 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  381 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  382 + <#elseif po.dictField?default("")?trim?length gt 1>
  383 + <#assign form_field_dictCode="${po.dictField}">
  384 + </#if>
  385 + {
  386 + label: '${po.filedComment}',
  387 + field: ${autoStringSuffix(po)},
  388 + <#if po.classType =='date'>
  389 + component: 'DatePicker',
  390 + <#elseif po.classType =='datetime'>
  391 + component: 'DatePicker',
  392 + componentProps: {
  393 + showTime:true,
  394 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
  395 + },
  396 + <#elseif po.classType =='time'>
  397 + component: 'TimePicker',
  398 + componentProps: {
  399 + valueFormat: 'HH:mm:ss'
  400 + },
  401 + <#elseif po.classType =='popup'>
  402 + <#include "/common/form/vue3popup.ftl">
  403 + <#elseif po.classType =='sel_depart'>
  404 + component: 'JSelectDept',
  405 + <#elseif po.classType =='switch'>
  406 + component: 'JSwitch',
  407 + componentProps:{
  408 + <#if po.dictField != 'is_open'>
  409 + options:${po.dictField}
  410 + </#if>
  411 + },
  412 + <#elseif po.classType =='pca'>
  413 + component: 'JAreaLinkage',
  414 + <#elseif po.classType =='markdown'>
  415 + component: 'JMarkdownEditor',//注意string转换问题
  416 + <#elseif po.classType =='password'>
  417 + component: 'InputPassword',
  418 + <#elseif po.classType =='sel_user'>
  419 + component: 'JSelectUserByDept',
  420 + componentProps:{
  421 + labelKey:'realname',
  422 + },
  423 + <#elseif po.classType =='textarea'>
  424 + component: 'InputTextArea',
  425 + <#elseif po.classType=='list' || po.classType=='radio'>
  426 + component: 'JDictSelectTag',
  427 + componentProps:{
  428 + dictCode:"${form_field_dictCode}"
  429 + },
  430 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  431 + component: 'JSelectMultiple',
  432 + componentProps:{
  433 + dictCode:"${form_field_dictCode}"
  434 + }
  435 + <#elseif po.classType=='sel_search'>
  436 + component: 'JSearchSelect',
  437 + componentProps:{
  438 + dict:"${form_field_dictCode}"
  439 + },
  440 +<#elseif po.classType=='cat_tree'>
  441 + <#assign form_cat_tree = true>
  442 + component: 'JCategorySelect',
  443 + componentProps:{
  444 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  445 + },
  446 + <#if po.dictText?default("")?trim?length gt 1>
  447 + <#assign form_cat_back = "${po.dictText}">
  448 + </#if>
  449 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  450 + component: 'InputNumber',
  451 + <#elseif po.classType=='file'>
  452 + component: 'JUpload',
  453 + componentProps:{
  454 + <#if po.uploadnum??>
  455 + maxCount:${po.uploadnum}
  456 + </#if>
  457 + },
  458 + <#elseif po.classType=='image'>
  459 + component: 'JImageUpload',
  460 + componentProps:{
  461 + <#if po.uploadnum??>
  462 + fileMax:${po.uploadnum}
  463 + </#if>
  464 + },
  465 + <#elseif po.classType=='umeditor'>
  466 + component: 'JEditor',
  467 + <#elseif po.classType == 'sel_tree'>
  468 + component: 'JTreeSelect',
  469 + componentProps:{
  470 + <#if po.dictText??>
  471 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  472 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  473 + <#elseif po.dictText?split(',')[1]??>
  474 + pidField:"${po.dictText?split(',')[1]}",
  475 + <#elseif po.dictText?split(',')[3]??>
  476 + hasChildField:"${po.dictText?split(',')[3]}",
  477 + </#if>
  478 + </#if>
  479 + pidValue:"${po.dictField}",
  480 + },
  481 + <#else>
  482 + component: 'Input',
  483 + </#if>
  484 + <#include "/common/utils.ftl">
  485 + <#if po.isShow == 'Y' && poHasCheck(po)>
  486 + dynamicRules: ({model,schema}) => {
  487 + <#if po.fieldName != 'id'>
  488 + <#assign fieldValidType = po.fieldValidType!''>
  489 + return [
  490 + <#-- 非空校验 -->
  491 + <#if po.nullable == 'N' || fieldValidType == '*'>
  492 + { required: true, message: '请输入${po.filedComment}!'},
  493 + <#elseif fieldValidType!=''>
  494 + { required: false},
  495 + </#if>
  496 + <#-- 唯一校验 -->
  497 + <#if fieldValidType == 'only'>
  498 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
  499 + <#-- 6到16位数字 -->
  500 + <#elseif fieldValidType == 'n6-16'>
  501 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  502 + <#-- 6到16位任意字符 -->
  503 + <#elseif fieldValidType == '*6-16'>
  504 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  505 + <#-- 6到18位字符串 -->
  506 + <#elseif fieldValidType == 's6-18'>
  507 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  508 + <#-- 网址 -->
  509 + <#elseif fieldValidType == 'url'>
  510 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  511 + <#-- 电子邮件 -->
  512 + <#elseif fieldValidType == 'e'>
  513 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  514 + <#-- 手机号码 -->
  515 + <#elseif fieldValidType == 'm'>
  516 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  517 + <#-- 邮政编码 -->
  518 + <#elseif fieldValidType == 'p'>
  519 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  520 + <#-- 字母 -->
  521 + <#elseif fieldValidType == 's'>
  522 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  523 + <#-- 数字 -->
  524 + <#elseif fieldValidType == 'n'>
  525 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  526 + <#-- 整数 -->
  527 + <#elseif fieldValidType == 'z'>
  528 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  529 + <#-- 金额 -->
  530 + <#elseif fieldValidType == 'money'>
  531 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  532 + <#-- 正则校验 -->
  533 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  534 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  535 + <#-- 无校验 -->
  536 + <#else>
  537 + <#t>
  538 + </#if>
  539 + ];
  540 + </#if>
  541 + },
  542 + </#if>
  543 + <#if po.readonly=='Y'>
  544 + dynamicDisabled:true
  545 + </#if>
  546 + },
  547 +</#if>
  548 +</#list>
  549 +<#if sub_id_exists == false>
  550 + {
  551 + label: '',
  552 + field: 'id',
  553 + component: 'Input',
  554 + show: false
  555 + },
  556 +</#if>
  557 +];
  558 +</#if>
  559 +</#list>
  560 +//子表表格配置
  561 +<#list subTables as sub>
  562 +<#if sub.foreignRelationType =='0'>
  563 +export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
  564 +<#assign popupBackFields = "">
  565 +
  566 +<#-- 循环子表的列 开始 -->
  567 +<#list sub.colums as col><#rt/>
  568 +<#if col.isShow =='Y'>
  569 +<#if col.filedComment !='外键' >
  570 + {
  571 + title: '${col.filedComment}',
  572 + key: '${autoStringSuffixForModel(col)}',
  573 +<#if col.classType =='date'>
  574 + type: JVxeTypes.date,
  575 + <#if col.readonly=='Y'>
  576 + disabled:true,
  577 + </#if>
  578 +<#elseif col.classType =='datetime'>
  579 + type: JVxeTypes.datetime,
  580 + <#if col.readonly=='Y'>
  581 + disabled:true,
  582 + </#if>
  583 +<#elseif col.classType =='time'>
  584 + type: JVxeTypes.time,
  585 + <#if col.readonly=='Y'>
  586 + disabled:true,
  587 + </#if>
  588 +<#elseif col.classType =='textarea'>
  589 + type: JVxeTypes.textarea,
  590 + <#if col.readonly=='Y'>
  591 + disabled:true,
  592 + </#if>
  593 +<#-- update-begin-author:taoyan date:20220523 for: VUEN-1084 【vue3】online表单测试发现的新问题 20、一对多列字段类型生成的不对,数字或者金额类型 -->
  594 +<#-- elseif "int,decimal,double,"?contains(col.classType) -->
  595 +<#elseif col.fieldDbType=='int' || col.fieldDbType=='double' || col.fieldDbType=='BigDecimal'>
  596 +<#-- update-end-author:taoyan date:20220523 for: VUEN-1084 【vue3】online表单测试发现的新问题 20、一对多列字段类型生成的不对,数字或者金额类型 -->
  597 + type: JVxeTypes.inputNumber,
  598 + <#if col.readonly=='Y'>
  599 + disabled:true,
  600 + </#if>
  601 +<#elseif col.classType =='list' || col.classType =='radio'>
  602 + type: JVxeTypes.select,
  603 + options:[],
  604 + <#if col.dictTable?default("")?trim?length gt 1>
  605 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  606 + <#else>
  607 + dictCode:"${col.dictField}",
  608 + </#if>
  609 + <#if col.readonly=='Y'>
  610 + disabled:true,
  611 + </#if>
  612 +<#elseif col.classType =='list_multi' || col.classType =='checkbox'>
  613 + type: JVxeTypes.selectMultiple,
  614 + options:[],
  615 + <#if col.dictTable?default("")?trim?length gt 1>
  616 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  617 + <#else>
  618 + dictCode:"${col.dictField}",
  619 + </#if>
  620 + <#if col.readonly=='Y'>
  621 + disabled:true,
  622 + </#if>
  623 +<#elseif col.classType =='sel_search'>
  624 + type: JVxeTypes.selectSearch,
  625 + <#if col.dictTable?default("")?trim?length gt 1>
  626 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  627 + <#else>
  628 + dictCode:"${col.dictField}",
  629 + </#if>
  630 + <#if col.readonly=='Y'>
  631 + disabled:true,
  632 + </#if>
  633 +<#elseif col.classType =='sel_depart'>
  634 + type: JVxeTypes.departSelect,
  635 + <#if col.readonly=='Y'>
  636 + disabled:true,
  637 + </#if>
  638 +<#elseif col.classType =='sel_user'>
  639 + type: JVxeTypes.userSelect,
  640 + <#if col.readonly=='Y'>
  641 + disabled:true,
  642 + </#if>
  643 +<#elseif col.classType =='image'>
  644 + type: JVxeTypes.image,
  645 + token:true,
  646 + responseName:"message",
  647 + <#if col.readonly=='Y'>
  648 + disabled:true,
  649 + </#if>
  650 + <#if col.uploadnum??>
  651 + number: ${col.uploadnum},
  652 + </#if>
  653 +<#elseif col.classType =='file'>
  654 + type: JVxeTypes.file,
  655 + token:true,
  656 + responseName:"message",
  657 + <#if col.readonly=='Y'>
  658 + disabled:true,
  659 + </#if>
  660 + <#if col.uploadnum??>
  661 + number: ${col.uploadnum},
  662 + </#if>
  663 +<#elseif col.classType =='switch'>
  664 + type: JVxeTypes.checkbox,
  665 + <#if col.dictField == 'is_open'>
  666 + customValue: ['Y', 'N'],
  667 + <#else>
  668 + customValue: ${col.dictField},
  669 + </#if>
  670 + <#if col.readonly=='Y'>
  671 + disabled:true,
  672 + </#if>
  673 +<#elseif col.classType =='popup'>
  674 +<#if popupBackFields?length gt 0>
  675 + <#assign popupBackFields = "${popupBackFields}"+","+"${col.dictText}">
  676 +<#else>
  677 + <#assign popupBackFields = "${col.dictText}">
  678 +</#if>
  679 + <#include "/common/form/vue3Jvxepopup.ftl">
  680 +<#else>
  681 + type: JVxeTypes.input,
  682 + <#if col.readonly=='Y'>
  683 + disabled:true,
  684 + </#if>
  685 +</#if>
  686 +<#if col.classType =='list_multi' || col.classType =='checkbox'>
  687 + width:"250px",
  688 +<#else>
  689 + width:"200px",
  690 +</#if>
  691 +<#if col.classType =='file'>
  692 + placeholder: '请选择文件',
  693 +<#else>
  694 + placeholder: '请输入${'$'}{title}',
  695 +</#if>
  696 +<#if col.defaultVal??>
  697 +<#if col.fieldDbType=="BigDecimal" || col.fieldDbType=="double" || col.fieldDbType=="int">
  698 + defaultValue:${col.defaultVal},
  699 + <#else>
  700 + defaultValue:"${col.defaultVal}",
  701 +</#if>
  702 +<#else>
  703 + defaultValue:'',
  704 +</#if>
  705 +<#-- 子表的校验 -->
  706 + <#include "/common/validatorRulesTemplate/sub-vue3.ftl">
  707 + },
  708 +</#if>
  709 +</#if>
  710 +</#list>
  711 +<#-- 循环子表的列 结束 -->
  712 + ]
  713 +</#if>
  714 +</#list>
0 715 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3Native/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3Native/components/${entityName}Form.vuei 0 → 100644
  1 +<#include "/common/utils.ftl">
  2 +<#assign need_category = false>
  3 +<#assign bpm_flag=false>
  4 +<#assign need_pca = false>
  5 +<#assign need_search = false>
  6 +<#assign need_dept_user = false>
  7 +<#assign need_switch = false>
  8 +<#assign need_dept = false>
  9 +<#assign need_multi = false>
  10 +<#assign need_popup = false>
  11 +<#assign need_select_tag = false>
  12 +<#assign need_select_tree = false>
  13 +<#assign need_time = false>
  14 +<#assign need_markdown = false>
  15 +<#assign need_upload = false>
  16 +<#assign need_image_upload = false>
  17 +<#assign need_editor = false>
  18 +<#assign need_checkbox = false>
  19 +<#assign form_span = 24>
  20 +<#if tableVo.fieldRowNum==2>
  21 + <#assign form_span = 12>
  22 +<#elseif tableVo.fieldRowNum==3>
  23 + <#assign form_span = 8>
  24 +<#elseif tableVo.fieldRowNum==4>
  25 + <#assign form_span = 6>
  26 +</#if>
  27 +<#assign hasOne2manyTable = false>
  28 +<#assign subTabActiveKey = ''>
  29 +<#assign subMainFieldMap={}>
  30 +<#assign subTableColumnsKey=[]>
  31 +<#assign hasOnlyValidate = false>
  32 +<template>
  33 + <a-spin :spinning="loading">
  34 + <a-form v-bind="formItemLayout">
  35 + <a-row>
  36 + <#list columns as po>
  37 + <#if po.isShow == 'Y' && po.fieldValidType?default("") == 'only'>
  38 + <#assign hasOnlyValidate = true>
  39 + </#if>
  40 + <#if po.fieldDbName=='bpm_status'>
  41 + <#assign bpm_flag=true>
  42 + </#if>
  43 +<#include "/common/form/native/vue3NativeForm.ftl">
  44 + </#list>
  45 + </a-row>
  46 + </a-form>
  47 +
  48 + <!-- 子表单区域 -->
  49 + <a-tabs v-model:activeKey="activeKey">
  50 +<#list subTables as sub><#rt/>
  51 + <a-tab-pane tab="${sub.ftlDescription}" key="${sub.entityName?uncap_first}" :forceRender="true">
  52 + <#if sub.foreignRelationType =='1'>
  53 + <${Format.humpToShortbar(sub.entityName)}-form ref="${sub.entityName?uncap_first}FormRef" :disabled="formDisabled"></${Format.humpToShortbar(sub.entityName)}-form>
  54 + <#else>
  55 + <j-vxe-table
  56 + :keep-source="true"
  57 + ref="${sub.entityName?uncap_first}TableRef"
  58 + :loading="${sub.entityName?uncap_first}Table.loading"
  59 + :columns="${sub.entityName?uncap_first}Table.columns"
  60 + :dataSource="${sub.entityName?uncap_first}Table.dataSource"
  61 + :maxHeight="300"
  62 + :disabled="formDisabled"
  63 + :rowNumber="true"
  64 + :rowSelection="true"
  65 + :toolbar="true"/>
  66 + </#if>
  67 + </a-tab-pane>
  68 +</#list>
  69 + </a-tabs>
  70 + </a-spin>
  71 +</template>
  72 +
  73 +<script lang="ts">
  74 + import { defineComponent, ref, reactive, computed, toRaw } from 'vue';
  75 + import { useValidateAntFormAndTable } from '/@/hooks/system/useJvxeMethods';
  76 + import { <#list subTables as sub><#if sub.foreignRelationType =='0'>query${sub.entityName}ListByMainId, </#if></#list>queryDataById, saveOrUpdate } from '../${entityName}.api';
  77 + <#list subTables as sub>
  78 + <#if sub.foreignRelationType =='1'>
  79 + import ${sub.entityName}Form from './${sub.entityName}Form.vue'
  80 + <#else>
  81 + <#assign hasOne2manyTable = true>
  82 + <#assign subTableColumnsKey += ['${sub.entityName?uncap_first}Columns']>
  83 + </#if>
  84 + <#if sub?? && (sub.foreignMainKeys)??>
  85 + <#list sub.foreignMainKeys as key>
  86 + <#assign subMainFieldMap += {"${sub.entityName}": "${dashedToCamel(key)}"}>
  87 + </#list>
  88 + </#if>
  89 + </#list>
  90 + <#if hasOne2manyTable == true>
  91 + import { JVxeTable } from '/@/components/jeecg/JVxeTable';
  92 + import {<#list subTableColumnsKey as columnsKey><#if columnsKey_index gt 0>, </#if>${columnsKey}</#list>} from '../${entityName}.data';
  93 + </#if>
  94 + <#include "/common/form/native/vue3NativeImport.ftl">
  95 + <#if hasOnlyValidate == true>
  96 + import { duplicateValidate } from '/@/utils/helper/validator'
  97 + </#if>
  98 + import { Form } from 'ant-design-vue';
  99 + const useForm = Form.useForm;
  100 +
  101 + export default defineComponent({
  102 + name: "${entityName}Form",
  103 + components:{
  104 + <#include "/common/form/native/vue3NativeComponents.ftl">
  105 + <#if hasOne2manyTable == true>
  106 + JVxeTable,
  107 + </#if>
  108 +<#list subTables as sub>
  109 + <#if sub.foreignRelationType =='1'>
  110 + ${sub.entityName}Form,
  111 + </#if>
  112 +</#list>
  113 + },
  114 + props:{
  115 + disabled:{
  116 + type: Boolean,
  117 + default: false
  118 + }
  119 + },
  120 + emits:['success'],
  121 + setup(props, {emit}) {
  122 + const loading = ref(false);
  123 + <#list subTables as sub>
  124 + <#if sub_index == 0>
  125 + <#assign subTabActiveKey = '${sub.entityName?uncap_first}'>
  126 + </#if>
  127 + <#if sub.foreignRelationType =='1'>
  128 + const ${sub.entityName?uncap_first}FormRef = ref();
  129 + <#else>
  130 + const ${sub.entityName?uncap_first}TableRef = ref();
  131 + const ${sub.entityName?uncap_first}Table = reactive<Record<string, any>>({
  132 + loading: false,
  133 + columns: ${sub.entityName?uncap_first}Columns,
  134 + dataSource: []
  135 + });
  136 + </#if>
  137 + </#list>
  138 + const activeKey = ref('${subTabActiveKey}');
  139 + const formData = reactive<Record<string, any>>({
  140 + <#list columns as po>
  141 + <#if po.isShow == 'Y'>
  142 + ${po.fieldName}: '',
  143 + </#if>
  144 + </#list>
  145 + });
  146 +
  147 + //表单验证
  148 + const validatorRules = reactive({
  149 + <#list columns as po>
  150 + <#if po.isShow == 'Y' && poHasCheck(po)>
  151 + ${po.fieldName}: [<#include "/common/validatorRulesTemplate/native/vue3CoreNative.ftl">],
  152 + </#if>
  153 + </#list>
  154 + });
  155 + const {resetFields, validate, validateInfos} = useForm(formData, validatorRules, {immediate: true});
  156 + const dbData = {};
  157 + const formItemLayout = {
  158 + labelCol: {xs: {span: 24}, sm: {span: 5}},
  159 + wrapperCol: {xs: {span: 24}, sm: {span: 16}},
  160 + };
  161 +
  162 + const formDisabled = computed(() => {
  163 + return props.disabled;
  164 + });
  165 +
  166 + function add() {
  167 + resetFields();
  168 + <#list subTables as sub>
  169 + <#if sub.foreignRelationType =='1'>
  170 + ${sub.entityName?uncap_first}FormRef.value.initFormData();
  171 + <#else>
  172 + ${sub.entityName?uncap_first}Table.dataSource = [];
  173 + </#if>
  174 + </#list>
  175 + }
  176 +
  177 + async function edit(row) {
  178 + //主表数据
  179 + await queryMainData(row.id);
  180 + //子表数据
  181 + <#list subTables as sub>
  182 + <#if sub.foreignRelationType =='1'>
  183 + await ${sub.entityName?uncap_first}FormRef.value.initFormData(row['${subMainFieldMap[sub.entityName]}']);
  184 + <#else>
  185 + const ${sub.entityName?uncap_first}DataList = await query${sub.entityName}ListByMainId(row['${subMainFieldMap[sub.entityName]}']);
  186 + ${sub.entityName?uncap_first}Table.dataSource = [...${sub.entityName?uncap_first}DataList];
  187 + </#if>
  188 + </#list>
  189 + }
  190 +
  191 + async function queryMainData(id) {
  192 + const row = await queryDataById(id);
  193 + Object.keys(row).map(k => {
  194 + formData[k] = row[k];
  195 + });
  196 + }
  197 +
  198 + const {getSubFormAndTableData, transformData} = useValidateAntFormAndTable(activeKey, {
  199 + <#list subTables as sub>
  200 + <#if sub.foreignRelationType =='1'>
  201 + '${sub.entityName?uncap_first}': ${sub.entityName?uncap_first}FormRef,
  202 + <#else>
  203 + '${sub.entityName?uncap_first}': ${sub.entityName?uncap_first}TableRef,
  204 + </#if>
  205 + </#list>
  206 + });
  207 +
  208 + async function getFormData() {
  209 + await validate();
  210 + return transformData(toRaw(formData))
  211 + }
  212 +
  213 + async function submitForm() {
  214 + const mainData = await getFormData();
  215 + const subData = await getSubFormAndTableData();
  216 + const values = Object.assign({}, dbData, mainData, subData);
  217 + console.log('表单提交数据', values)
  218 + const isUpdate = values.id ? true : false
  219 + await saveOrUpdate(values, isUpdate);
  220 + //关闭弹窗
  221 + emit('success');
  222 + }
  223 +
  224 + function setFieldsValue(values) {
  225 + if(values){
  226 + Object.keys(values).map(k=>{
  227 + formData[k] = values[k];
  228 + });
  229 + }
  230 + }
  231 +
  232 + <#list columns as po>
  233 + <#if po.isShow == 'Y' && po.fieldValidType?default("") == 'only'>
  234 + async function ${po.fieldName}Duplicatevalidate(_r, value) {
  235 + return duplicateValidate('${tableName}', '${po.fieldDbName}', value, formData.id || '')
  236 + }
  237 + </#if>
  238 + </#list>
  239 +
  240 + return {
  241 + <#list subTables as sub>
  242 + <#if sub.foreignRelationType =='1'>
  243 + ${sub.entityName?uncap_first}FormRef,
  244 + <#else>
  245 + ${sub.entityName?uncap_first}TableRef,
  246 + ${sub.entityName?uncap_first}Table,
  247 + </#if>
  248 + </#list>
  249 + validatorRules,
  250 + validateInfos,
  251 + activeKey,
  252 + loading,
  253 + formData,
  254 + setFieldsValue,
  255 + formItemLayout,
  256 + formDisabled,
  257 + getFormData,
  258 + submitForm,
  259 + add,
  260 + edit
  261 + }
  262 + }
  263 + });
  264 +</script>
0 265 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3Native/components/${entityName}Modal.vuei 0 → 100644
  1 +<#include "/common/utils.ftl">
  2 +<template>
  3 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" :width="${getModalWidth(tableVo.fieldRowNum?default(1))}" @ok="handleSubmit">
  4 + <${Format.humpToShortbar(entityName)}-form ref="formComponent" :disabled="formDisabled" @success="submitSuccess"></${Format.humpToShortbar(entityName)}-form>
  5 + </BasicModal>
  6 +</template>
  7 +
  8 +<script lang="ts">
  9 + import { ref, unref } from 'vue';
  10 + import { BasicModal, useModalInner } from '/@/components/Modal';
  11 + import ${entityName}Form from './${entityName}Form.vue';
  12 +
  13 + export default {
  14 + name: "TestCgMainVxeModal",
  15 + components:{
  16 + BasicModal,
  17 + ${entityName}Form
  18 + },
  19 + emits:['register','success'],
  20 + setup(_p, {emit}){
  21 + const formComponent = ref()
  22 + const isUpdate = ref(true);
  23 + const formDisabled = ref(false);
  24 + const title = ref('')
  25 +
  26 + //表单赋值
  27 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  28 + setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
  29 + isUpdate.value = !!data?.isUpdate;
  30 + title.value = data?.isUpdate?'编辑':'新增'
  31 + formDisabled.value = !data?.showFooter;
  32 + if (unref(isUpdate)) {
  33 + console.log('data', data)
  34 + formComponent.value.edit(data.record)
  35 + }else{
  36 + formComponent.value.add()
  37 + }
  38 + });
  39 +
  40 + function handleSubmit() {
  41 + formComponent.value.submitForm();
  42 + }
  43 +
  44 + function submitSuccess(){
  45 + emit('success');
  46 + closeModal();
  47 + }
  48 +
  49 + return {
  50 + registerModal,
  51 + title,
  52 + formComponent,
  53 + formDisabled,
  54 + handleSubmit,
  55 + submitSuccess
  56 + }
  57 + }
  58 + }
  59 +</script>
  60 +<style lang="less" scoped>
  61 + /** 时间和数字输入框样式 */
  62 + :deep(.ant-input-number){
  63 + width: 100%
  64 + }
  65 +
  66 + :deep(.ant-calendar-picker){
  67 + width: 100%
  68 + }
  69 +</style>
0 70 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3Native/components/[1-n]Form.vuei 0 → 100644
  1 +<#include "/common/utils.ftl">
  2 +<#list subTables as sub>
  3 +<#if sub.foreignRelationType=='1'>
  4 +#segment#${sub.entityName}Form.vue
  5 +<#include "/common/utils.ftl">
  6 +<#assign need_category = false>
  7 +<#assign bpm_flag=false>
  8 +<#assign need_pca = false>
  9 +<#assign need_search = false>
  10 +<#assign need_dept_user = false>
  11 +<#assign need_switch = false>
  12 +<#assign need_dept = false>
  13 +<#assign need_multi = false>
  14 +<#assign need_popup = false>
  15 +<#assign need_select_tag = false>
  16 +<#assign need_select_tree = false>
  17 +<#assign need_time = false>
  18 +<#assign need_markdown = false>
  19 +<#assign need_upload = false>
  20 +<#assign need_image_upload = false>
  21 +<#assign need_editor = false>
  22 +<#assign need_checkbox = false>
  23 +<#assign form_span = 24>
  24 +<#if tableVo.fieldRowNum==2>
  25 + <#assign form_span = 12>
  26 +<#elseif tableVo.fieldRowNum==3>
  27 + <#assign form_span = 8>
  28 +<#elseif tableVo.fieldRowNum==4>
  29 + <#assign form_span = 6>
  30 +</#if>
  31 + <#assign hasOnlyValidate = false>
  32 +<template>
  33 + <a-spin :spinning="loading">
  34 + <a-form v-bind="formItemLayout">
  35 + <a-row>
  36 + <#list sub.colums as po>
  37 + <#if po.isShow == 'Y' && po.fieldValidType?default("") == 'only'>
  38 + <#assign hasOnlyValidate = true>
  39 + </#if>
  40 + <#if po.fieldDbName=='bpm_status'>
  41 + <#assign bpm_flag=true>
  42 + </#if>
  43 + <#include "/common/form/native/vue3NativeForm.ftl">
  44 + </#list>
  45 + </a-row>
  46 + </a-form>
  47 + </a-spin>
  48 +</template>
  49 +
  50 +<script lang="ts">
  51 + import { defineComponent, ref, reactive, toRaw } from 'vue';
  52 + import { query${sub.entityName}ListByMainId } from '../${entityName}.api';
  53 +<#include "/common/form/native/vue3NativeImport.ftl">
  54 +<#if hasOnlyValidate == true>
  55 + import { duplicateValidate } from '/@/utils/helper/validator'
  56 +</#if>
  57 + import { useMessage } from '/@/hooks/web/useMessage';
  58 + import { Form } from 'ant-design-vue';
  59 + const useForm = Form.useForm;
  60 +
  61 + export default defineComponent({
  62 + name: '${sub.entityName}Form',
  63 + components:{
  64 + <#include "/common/form/native/vue3NativeComponents.ftl">
  65 + },
  66 + props:{
  67 + disabled:{
  68 + type: Boolean,
  69 + default: false
  70 + }
  71 + },
  72 + setup(){
  73 + const { createMessage } = useMessage();
  74 + const isForm = true;
  75 + const loading = ref(false);
  76 + const formData = reactive<Record<string, any>>({
  77 + <#list sub.colums as po>
  78 + <#if po.isShow == 'Y'>
  79 + ${po.fieldName}: '',
  80 + </#if>
  81 + </#list>
  82 + });
  83 + //表单验证
  84 + const validatorRules = reactive({
  85 + <#list sub.colums as po>
  86 + <#if po.isShow == 'Y' && poHasCheck(po)>
  87 + ${po.fieldName}: [<#include "/common/validatorRulesTemplate/native/vue3CoreNative.ftl">],
  88 + </#if>
  89 + </#list>
  90 + })
  91 + const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, {immediate: true});
  92 + const formItemLayout = {
  93 + labelCol: { xs: { span: 24 }, sm: { span: 5 } },
  94 + wrapperCol: { xs: { span: 24 }, sm: { span: 16 } },
  95 + };
  96 +
  97 + async function initFormData(mainId) {
  98 + resetFields();
  99 + if(mainId){
  100 + let list = await query${sub.entityName}ListByMainId(mainId);
  101 + if(list && list.length>0){
  102 + let temp = list[0];
  103 + Object.keys(temp).map(k=>{
  104 + formData[k] = temp[k];
  105 + })
  106 + }
  107 + }
  108 + }
  109 +
  110 + async function getFormData() {
  111 + await validate();
  112 + let subFormData = toRaw(formData);
  113 + if(Object.keys(subFormData).length>0){
  114 + return subFormData
  115 + }
  116 + return false;
  117 + }
  118 +
  119 + function setFieldsValue(values) {
  120 + if(values){
  121 + Object.keys(values).map(k=>{
  122 + formData[k] = values[k];
  123 + });
  124 + }
  125 + }
  126 +
  127 + <#list sub.colums as po>
  128 + <#if po.isShow == 'Y' && po.fieldValidType?default("") == 'only'>
  129 + async function ${po.fieldName}Duplicatevalidate(_r, value) {
  130 + return duplicateValidate('${sub.tableName}', '${po.fieldDbName}', value, formData.id || '')
  131 + }
  132 + </#if>
  133 + </#list>
  134 +
  135 + return {
  136 + loading,
  137 + formData,
  138 + formItemLayout,
  139 + initFormData,
  140 + getFormData,
  141 + setFieldsValue,
  142 + isForm,
  143 + validateInfos
  144 + }
  145 + }
  146 + });
  147 +</script>
  148 +</#if>
  149 +</#list>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -196,17 +196,14 @@ public class ${entityName}Controller {
196 196 QueryWrapper<${entityName}> queryWrapper = QueryGenerator.initQueryWrapper(${entityName?uncap_first}, request.getParameterMap());
197 197 LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
198 198  
199   - //Step.2 获取导出数据
200   - List<${entityName}> queryList = ${entityName?uncap_first}Service.list(queryWrapper);
201   - // 过滤选中数据
202   - String selections = request.getParameter("selections");
203   - List<${entityName}> ${entityName?uncap_first}List = new ArrayList<${entityName}>();
204   - if(oConvertUtils.isEmpty(selections)) {
205   - ${entityName?uncap_first}List = queryList;
206   - }else {
207   - List<String> selectionList = Arrays.asList(selections.split(","));
208   - ${entityName?uncap_first}List = queryList.stream().filter(item -> selectionList.contains(item.getId())).collect(Collectors.toList());
209   - }
  199 + //配置选中数据查询条件
  200 + String selections = request.getParameter("selections");
  201 + if(oConvertUtils.isNotEmpty(selections)) {
  202 + List<String> selectionList = Arrays.asList(selections.split(","));
  203 + queryWrapper.in("id",selectionList);
  204 + }
  205 + //Step.2 获取导出数据
  206 + List<${entityName}> ${entityName?uncap_first}List = ${entityName?uncap_first}Service.list(queryWrapper);
210 207  
211 208 // Step.3 组装pageList
212 209 List<${entityName}Page> pageList = new ArrayList<${entityName}Page>();
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
  1 +<#include "/common/utils.ftl">
1 2 package ${bussiPackage}.${entityPackage}.entity;
2 3  
3 4 import java.io.Serializable;
... ... @@ -31,9 +32,9 @@ public class ${entityName} implements Serializable {
31 32 <#-- 生成字典Code -->
32 33 <#assign list_field_dictCode="">
33 34 <#if po.classType='sel_user'>
34   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  35 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
35 36 <#elseif po.classType='sel_depart'>
36   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  37 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
37 38 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
38 39 <#if po.dictTable?default("")?trim?length gt 1>
39 40 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/entity/[1-n]Entity.javai
  1 +<#include "/common/utils.ftl">
1 2 <#list subTables as subTab>
2 3 #segment#${subTab.entityName}.java
3 4 package ${bussiPackage}.${entityPackage}.entity;
... ... @@ -32,9 +33,9 @@ public class ${subTab.entityName} implements Serializable {
32 33 <#-- 生成字典Code -->
33 34 <#assign list_field_dictCode="">
34 35 <#if po.classType='sel_user'>
35   - <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "realname", dicCode = "username"'>
  36 + <#assign list_field_dictCode=', dictTable = "sys_user", dicText = "${camelToDashed(po.extendParams.text?default(\"realname\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"username\")?trim)}"'>
36 37 <#elseif po.classType='sel_depart'>
37   - <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "depart_name", dicCode = "id"'>
  38 + <#assign list_field_dictCode=', dictTable = "sys_depart", dicText = "${camelToDashed(po.extendParams.text?default(\"depart_name\")?trim)}", dicCode = "${camelToDashed(po.extendParams.store?default(\"id\")?trim)}"'>
38 39 <#elseif po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox'>
39 40 <#if po.dictTable?default("")?trim?length gt 1>
40 41 <#assign list_field_dictCode=', dictTable = "${po.dictTable}", dicText = "${po.dictText}", dicCode = "${po.dictField}"'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Form.vuei
... ... @@ -39,7 +39,7 @@
39 39 @input="popupCallback"
40 40 <#if po.readonly=='Y'>disabled</#if>/>
41 41 <#elseif po.classType =='sel_depart'>
42   - <j-select-depart v-model="model.${po.fieldName}" multi <#if po.readonly=='Y'>disabled</#if>/>
  42 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
43 43 <#elseif po.classType =='switch'>
44 44 <j-switch v-model="model.${po.fieldName}" <#if po.dictField!= 'is_open'>:options="${po.dictField}"</#if> <#if po.readonly=='Y'>disabled</#if>></j-switch>
45 45 <#elseif po.classType =='pca'>
... ... @@ -49,7 +49,7 @@
49 49 <#elseif po.classType =='password'>
50 50 <a-input-password v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
51 51 <#elseif po.classType =='sel_user'>
52   - <j-select-user-by-dep v-model="model.${po.fieldName}" <#if po.readonly=='Y'>disabled</#if>/>
  52 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
53 53 <#elseif po.classType =='textarea'>
54 54 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}" <#if po.readonly=='Y'>disabled</#if>/>
55 55 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/[1-n]Form.vuei
... ... @@ -44,7 +44,7 @@
44 44 :multi="${po.extendParams.popupMulti?c}"
45 45 @input="popupCallback"/>
46 46 <#elseif po.classType =='sel_depart'>
47   - <j-select-depart v-model="model.${po.fieldName}" multi/>
  47 + <j-select-depart v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if> />
48 48 <#elseif po.classType =='switch'>
49 49 <j-switch v-model="model.${po.fieldName}" <#if po.dictField!= 'is_open'>:options="${po.dictField}"</#if>></j-switch>
50 50 <#elseif po.classType =='pca'>
... ... @@ -54,7 +54,7 @@
54 54 <#elseif po.classType =='password'>
55 55 <a-input-password v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}"/>
56 56 <#elseif po.classType =='sel_user'>
57   - <j-select-user-by-dep v-model="model.${po.fieldName}"/>
  57 + <j-select-user-by-dep v-model="model.${po.fieldName}" :multi="${po.extendParams.multi?default('true')}"<#if po.extendParams.store?default("")?trim?length gt 0> store="${po.extendParams.store}"</#if><#if po.extendParams.text?default("")?trim?length gt 0> text="${po.extendParams.text}"</#if> <#if po.readonly=='Y'>disabled</#if>/>
58 58 <#elseif po.classType =='textarea'>
59 59 <a-textarea v-model="model.${autoStringSuffixForModel(po)}" rows="4" placeholder="请输入${po.filedComment}"/>
60 60 <#elseif po.classType=='list' || po.classType=='radio'>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei
... ... @@ -46,6 +46,10 @@
46 46 <template #htmlSlot="{text}">
47 47 <div v-html="text"></div>
48 48 </template>
  49 + <!--省市区字段回显插槽-->
  50 + <template #pcaSlot="{text}">
  51 + {{ getAreaTextByCode(text) }}
  52 + </template>
49 53 <template #fileSlot="{text}">
50 54 <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
51 55 <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
... ... @@ -64,6 +68,10 @@
64 68 import ${entityName}Modal from './components/${entityName}Modal.vue'
65 69 import {columns, searchFormSchema} from './${entityName}.data';
66 70 import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName}.api';
  71 + import {downloadFile} from '/@/utils/common/renderUtils';
  72 +<#if list_need_pca>
  73 + import { getAreaTextByCode } from '/@/components/Form/src/utils/Area';
  74 +</#if>
67 75 <#if list_need_category>
68 76 import { loadCategoryData } from '/@/api/common/api'
69 77 import { getAuthCache, setAuthCache } from '/@/utils/auth';
... ... @@ -84,6 +92,17 @@
84 92 schemas: searchFormSchema,
85 93 autoSubmitOnEnter:true,
86 94 showAdvancedButton:true,
  95 + fieldMapToNumber: [
  96 + <#list columns as po>
  97 + <#if po.isQuery=='Y'>
  98 + <#if po.queryMode!='single'>
  99 + <#if po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  100 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end']],
  101 + </#if>
  102 + </#if>
  103 + </#if>
  104 + </#list>
  105 + ],
87 106 fieldMapToTime: [
88 107 <#list columns as po>
89 108 <#if po.isQuery=='Y'>
... ... @@ -98,9 +117,10 @@
98 117 </#list>
99 118 ],
100 119 },
101   - actionColumn: {
  120 + actionColumn: {
102 121 width: 120,
103   - },
  122 + fixed:'right'
  123 + },
104 124 },
105 125 exportConfig: {
106 126 name:"${tableVo.ftlDescription}",
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi
  1 +<#include "/common/utils.ftl">
1 2 import {BasicColumn} from '/@/components/Table';
2 3 import {FormSchema} from '/@/components/Table';
3 4 import { rules} from '/@/utils/helper/validator';
... ... @@ -25,7 +26,7 @@ export const columns: BasicColumn[] = [
25 26 slots: { customRender: 'htmlSlot' },
26 27 <#elseif po.classType=='pca'>
27 28 dataIndex: '${po.fieldName}',
28   - slots: { customRender: 'pcaSlot' },//TODO 未翻译
  29 + slots: { customRender: 'pcaSlot' },
29 30 <#elseif po.classType=='file'>
30 31 dataIndex: '${po.fieldName}',
31 32 slots: { customRender: 'fileSlot' },
... ... @@ -57,7 +58,7 @@ export const columns: BasicColumn[] = [
57 58 return render.renderCategoryTree(text,'${po.dictField?default("")}')
58 59 },
59 60 <#else>
60   - customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  61 + customRender: ({text, record}) => (text ? record['${po.dictText}'] : '')
61 62 </#if>
62 63 <#else>
63 64 dataIndex: '${po.fieldName}'
... ... @@ -95,16 +96,22 @@ export const searchFormSchema: FormSchema[] = [
95 96 <#elseif po.classType=='switch'>
96 97 component: 'JSwitch',
97 98 componentProps:{
  99 + query:true,
98 100 <#if po.dictField != 'is_open'>
99   - options:"${po.dictField}"
  101 + options:${po.dictField}
100 102 </#if>
101 103 },
102 104 <#elseif po.classType=='sel_depart'>
103 105 component: 'JSelectDept',
104 106 <#elseif po.classType=='list_multi'>
105   - component: 'JMultiSelectTag',//暂无该组件
  107 + component: 'JSelectMultiple',
106 108 componentProps:{
107   - dictCode:"query_field_dictCode?default("")"
  109 + <#if po.dictTable?default("")?trim?length gt 1>
  110 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}",
  111 + <#elseif po.dictField?default("")?trim?length gt 1>
  112 + dictCode:"${po.dictField}",
  113 + </#if>
  114 + triggerChange: true
108 115 },
109 116 <#elseif po.classType=='cat_tree'>
110 117 component: 'JCategorySelect',
... ... @@ -121,16 +128,7 @@ export const searchFormSchema: FormSchema[] = [
121 128 <#elseif po.classType=='pca'>
122 129 component: 'JAreaLinkage',
123 130 <#elseif po.classType=='popup'>
124   - component: 'JPopup',
125   - componentProps: ({ formActionType }) => {
126   - const {setFieldsValue} = formActionType;
127   - return{
128   - setFieldsValue:setFieldsValue,
129   - code:"${po.dictTable}",
130   - fieldConfig:"${po.dictField}",
131   - multi:${po.extendParams.popupMulti?c},
132   - }
133   - },
  131 + <#include "/common/form/vue3popup.ftl">
134 132 <#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
135 133 <#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
136 134 component: 'JDictSelectTag',
... ... @@ -157,6 +155,8 @@ export const searchFormSchema: FormSchema[] = [
157 155 componentProps: {
158 156 showTime:true
159 157 },
  158 +<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  159 + component: 'JRangeNumber',
160 160 <#else>
161 161 component: 'Input', //TODO 范围查询
162 162 </#if>
... ... @@ -189,27 +189,19 @@ export const formSchema: FormSchema[] = [
189 189 </#if>
190 190 {
191 191 label: '${po.filedComment}',
192   - field: '${po.fieldName}',
  192 + field: ${autoStringSuffix(po)},
193 193 <#if po.classType =='date'>
194 194 component: 'DatePicker',
195   - <#elseif po.fieldType =='datetime'>
  195 + <#elseif po.classType =='datetime'>
196 196 component: 'DatePicker',
197 197 componentProps: {
198   - showTime:true
  198 + showTime:true,
  199 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
199 200 },
200   - <#elseif po.fieldType =='time'>
  201 + <#elseif po.classType =='time'>
201 202 component: 'TimePicker',
202 203 <#elseif po.classType =='popup'>
203   - component: 'JPopup',
204   - componentProps: ({ formActionType }) => {
205   - const {setFieldsValue} = formActionType;
206   - return{
207   - setFieldsValue:setFieldsValue,
208   - code:"${po.dictTable}",
209   - fieldConfig:${po.dictField},
210   - multi:${po.extendParams.popupMulti?c},
211   - }
212   - },
  204 + <#include "/common/form/vue3popup.ftl">
213 205 <#elseif po.classType =='sel_depart'>
214 206 component: 'JSelectDept',
215 207 <#elseif po.classType =='switch'>
... ... @@ -231,14 +223,14 @@ export const formSchema: FormSchema[] = [
231 223 labelKey:'realname',
232 224 },
233 225 <#elseif po.classType =='textarea'>
234   - component: 'InputTextArea',//TODO 注意string转换问题
  226 + component: 'InputTextArea',
235 227 <#elseif po.classType=='list' || po.classType=='radio'>
236 228 component: 'JDictSelectTag',
237 229 componentProps:{
238 230 dictCode:"${form_field_dictCode}"
239 231 },
240 232 <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
241   - component: 'JMultiSelectTag',//TODO 暂无该组件
  233 + component: 'JSelectMultiple',
242 234 componentProps:{
243 235 dictCode:"${form_field_dictCode}"
244 236 },
... ... @@ -273,7 +265,7 @@ export const formSchema: FormSchema[] = [
273 265 </#if>
274 266 },
275 267 <#elseif po.classType=='umeditor'>
276   - component: 'JCodeEditor', //TODO String后缀暂未添加
  268 + component: 'JEditor',
277 269 <#elseif po.classType == 'sel_tree'>
278 270 component: 'JTreeSelect',
279 271 componentProps:{
... ... @@ -305,16 +297,16 @@ export const formSchema: FormSchema[] = [
305 297 </#if>
306 298 <#-- 唯一校验 -->
307 299 <#if fieldValidType == 'only'>
308   - {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  300 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
309 301 <#-- 6到16位数字 -->
310 302 <#elseif fieldValidType == 'n6-16'>
311 303 { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
312 304 <#-- 6到16位任意字符 -->
313 305 <#elseif fieldValidType == '*6-16'>
314 306 { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
315   - <#-- 6到18位字符串 -->
  307 + <#-- 6到18位字 -->
316 308 <#elseif fieldValidType == 's6-18'>
317   - { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  309 + { pattern: /^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
318 310 <#-- 网址 -->
319 311 <#elseif fieldValidType == 'url'>
320 312 { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
... ... @@ -373,10 +365,14 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
373 365 <#assign form_cat_tree = false>
374 366 <#assign form_cat_back = "">
375 367 <#assign bpm_flag=false>
  368 +<#assign sub_id_exists = false>
376 369 <#list sub.colums as po><#rt/>
377 370 <#if po.fieldDbName=='bpm_status'>
378 371 <#assign bpm_flag=true>
379 372 </#if>
  373 +<#if po.fieldDbName == 'id'>
  374 + <#assign sub_id_exists = true>
  375 +</#if>
380 376 <#if po.isShow =='Y'>
381 377 <#assign form_field_dictCode="">
382 378 <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
... ... @@ -386,27 +382,19 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
386 382 </#if>
387 383 {
388 384 label: '${po.filedComment}',
389   - field: '${po.fieldName}',
  385 + field: ${autoStringSuffix(po)},
390 386 <#if po.classType =='date'>
391 387 component: 'DatePicker',
392   - <#elseif po.fieldType =='datetime'>
  388 + <#elseif po.classType =='datetime'>
393 389 component: 'DatePicker',
394 390 componentProps: {
395   - showTime:true
  391 + showTime:true,
  392 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
396 393 },
397   - <#elseif po.fieldType =='time'>
  394 + <#elseif po.classType =='time'>
398 395 component: 'TimePicker',
399 396 <#elseif po.classType =='popup'>
400   - component: 'JPopup',
401   - componentProps: ({ formActionType }) => {
402   - const {setFieldsValue} = formActionType;
403   - return{
404   - setFieldsValue:setFieldsValue,
405   - code:"${po.dictTable}",
406   - fieldConfig:${po.dictField},
407   - multi:${po.extendParams.popupMulti?c},
408   - }
409   - },
  397 + <#include "/common/form/vue3popup.ftl">
410 398 <#elseif po.classType =='sel_depart'>
411 399 component: 'JSelectDept',
412 400 <#elseif po.classType =='switch'>
... ... @@ -428,14 +416,14 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
428 416 labelKey:'realname',
429 417 },
430 418 <#elseif po.classType =='textarea'>
431   - component: 'InputTextArea',//TODO 注意string转换问题
  419 + component: 'InputTextArea',
432 420 <#elseif po.classType=='list' || po.classType=='radio'>
433 421 component: 'JDictSelectTag',
434 422 componentProps:{
435 423 dictCode:"${form_field_dictCode}"
436 424 },
437 425 <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
438   - component: 'JMultiSelectTag',//TODO 暂无该组件
  426 + component: 'JSelectMultiple',
439 427 componentProps:{
440 428 dictCode:"${form_field_dictCode}"
441 429 },
... ... @@ -470,7 +458,7 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
470 458 </#if>
471 459 },
472 460 <#elseif po.classType=='umeditor'>
473   - component: 'JCodeEditor', //TODO String后缀暂未添加
  461 + component: 'JEditor',
474 462 <#elseif po.classType == 'sel_tree'>
475 463 component: 'JTreeSelect',
476 464 componentProps:{
... ... @@ -502,16 +490,16 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
502 490 </#if>
503 491 <#-- 唯一校验 -->
504 492 <#if fieldValidType == 'only'>
505   - {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  493 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema)[0]},
506 494 <#-- 6到16位数字 -->
507 495 <#elseif fieldValidType == 'n6-16'>
508 496 { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
509 497 <#-- 6到16位任意字符 -->
510 498 <#elseif fieldValidType == '*6-16'>
511 499 { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
512   - <#-- 6到18位字符串 -->
  500 + <#-- 6到18位字 -->
513 501 <#elseif fieldValidType == 's6-18'>
514   - { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  502 + { pattern: /^[a-z|A-Z]{6,18}$/, message: '请输入6到18位字母!'},
515 503 <#-- 网址 -->
516 504 <#elseif fieldValidType == 'url'>
517 505 { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
... ... @@ -553,6 +541,14 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
553 541 },
554 542 </#if>
555 543 </#list>
  544 +<#if sub_id_exists == false>
  545 + {
  546 + label: '',
  547 + field: 'id',
  548 + component: 'Input',
  549 + show: false
  550 + },
  551 +</#if>
556 552 ];
557 553 </#if>
558 554 </#list>
... ... @@ -579,12 +575,20 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
579 575 <#if col.readonly=='Y'>
580 576 disabled:true,
581 577 </#if>
  578 +<#elseif col.classType =='time'>
  579 + type: JVxeTypes.time,
  580 + <#if col.readonly=='Y'>
  581 + disabled:true,
  582 + </#if>
582 583 <#elseif col.classType =='textarea'>
583 584 type: JVxeTypes.textarea,
584 585 <#if col.readonly=='Y'>
585 586 disabled:true,
586 587 </#if>
587   -<#elseif "int,decimal,double,"?contains(col.classType)>
  588 +<#-- update-begin-author:taoyan date:20220523 for: VUEN-1084 【vue3】online表单测试发现的新问题 20、一对多列字段类型生成的不对,数字或者金额类型 -->
  589 +<#-- elseif "int,decimal,double,"?contains(col.classType) -->
  590 +<#elseif col.fieldDbType=='int' || col.fieldDbType=='double' || col.fieldDbType=='BigDecimal'>
  591 +<#-- update-end-author:taoyan date:20220523 for: VUEN-1084 【vue3】online表单测试发现的新问题 20、一对多列字段类型生成的不对,数字或者金额类型 -->
588 592 type: JVxeTypes.inputNumber,
589 593 <#if col.readonly=='Y'>
590 594 disabled:true,
... ... @@ -621,6 +625,16 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
621 625 <#if col.readonly=='Y'>
622 626 disabled:true,
623 627 </#if>
  628 +<#elseif col.classType =='sel_depart'>
  629 + type: JVxeTypes.departSelect,
  630 + <#if col.readonly=='Y'>
  631 + disabled:true,
  632 + </#if>
  633 +<#elseif col.classType =='sel_user'>
  634 + type: JVxeTypes.userSelect,
  635 + <#if col.readonly=='Y'>
  636 + disabled:true,
  637 + </#if>
624 638 <#elseif col.classType =='image'>
625 639 type: JVxeTypes.image,
626 640 token:true,
... ... @@ -644,9 +658,9 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
644 658 <#elseif col.classType =='switch'>
645 659 type: JVxeTypes.checkbox,
646 660 <#if col.dictField == 'is_open'>
647   - customValue: ['Y', 'N'],
  661 + customValue: ['Y', 'N'],
648 662 <#else>
649   - customValue: ${col.dictField},
  663 + customValue: ${col.dictField},
650 664 </#if>
651 665 <#if col.readonly=='Y'>
652 666 disabled:true,
... ... @@ -657,14 +671,7 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
657 671 <#else>
658 672 <#assign popupBackFields = "${col.dictText}">
659 673 </#if>
660   - type: JVxeTypes.popup,
661   - popupCode:"${col.dictTable}",
662   - field:"${col.dictField}",
663   - orgFields:"${col.dictField}",
664   - destFields:"${Format.underlineToHump(col.dictText)}",
665   - <#if col.readonly=='Y'>
666   - disabled:true,
667   - </#if>
  674 + <#include "/common/form/vue3Jvxepopup.ftl">
668 675 <#else>
669 676 type: JVxeTypes.input,
670 677 <#if col.readonly=='Y'>
... ... @@ -691,18 +698,7 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
691 698 defaultValue:'',
692 699 </#if>
693 700 <#-- 子表的校验 -->
694   -<#assign subFieldValidType = col.fieldValidType!''>
695   -<#-- 非空校验 -->
696   -<#if col.nullable == 'N' || subFieldValidType == '*'>
697   - validateRules: [{ required: true, message: '${'$'}{title}不能为空' }],
698   -<#-- 其他情况下,只要有值就被认为是正则校验 -->
699   -<#elseif subFieldValidType?length gt 0>
700   -<#assign subMessage = '格式不正确'>
701   -<#if subFieldValidType == 'only' >
702   - <#assign subMessage = '不能重复'>
703   -</#if>
704   - validateRules: [{ pattern: "${subFieldValidType}", message: "${'$'}{title}${subMessage}" }],
705   -</#if>
  701 + <#include "/common/validatorRulesTemplate/sub-vue3.ftl">
706 702 },
707 703 </#if>
708 704 </#if>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}_menu_insert.sql 0 → 100644
  1 +<#include "/common/sql/menu_insert.ftl">
0 2 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei
  1 +<#include "/common/utils.ftl">
1 2 <template>
2   - <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  3 + <BasicModal ref="modalRef" destroyOnClose wrapClassName="j-cgform-tab-modal" v-bind="$attrs" @register="registerModal" :width="${getModalWidth(tableVo.fieldRowNum?default(1))}" @ok="handleSubmit">
3 4 <!-- 子表单区域 -->
4 5 <a-tabs v-model:activeKey="activeKey" @change="handleChangeTabs">
5 6 <!--主表区域 -->
6   - <a-tab-pane tab="${tableVo.ftlDescription}" :key="refKeys[0]" :forceRender="true">
  7 + <a-tab-pane tab="${tableVo.ftlDescription}" :key="refKeys[0]" :forceRender="true" :style="tabsStyle">
7 8 <BasicForm @register="registerForm" ref="formRef"/>
8 9 </a-tab-pane>
9 10 <!--子表单区域 -->
10 11 <#list subTables as sub><#rt/>
11 12 <#assign refKey = sub.entityName?uncap_first/>
12 13 <#if sub.foreignRelationType =='1'>
13   - <a-tab-pane tab="${sub.ftlDescription}" key="${refKey}" :forceRender="true">
14   - <${sub.entityName}Form ref="${sub.entityName?uncap_first}Form"></${sub.entityName}Form>
  14 + <a-tab-pane tab="${sub.ftlDescription}" key="${refKey}" :forceRender="true" :style="tabsStyle">
  15 + <${sub.entityName}Form ref="${sub.entityName?uncap_first}Form" :disabled="formDisabled"></${sub.entityName}Form>
15 16 </a-tab-pane>
16 17  
17 18 <#else>
18   - <a-tab-pane tab="${sub.ftlDescription}" key="${refKey}" :forceRender="true">
  19 + <a-tab-pane tab="${sub.ftlDescription}" key="${refKey}" :forceRender="true" :style="tabsStyle">
19 20 <JVxeTable
20 21 keep-source
21 22 resizable
... ... @@ -23,7 +24,8 @@
23 24 :loading="${sub.entityName?uncap_first}Table.loading"
24 25 :columns="${sub.entityName?uncap_first}Table.columns"
25 26 :dataSource="${sub.entityName?uncap_first}Table.dataSource"
26   - :maxHeight="300"
  27 + :height="340"
  28 + :disabled="formDisabled"
27 29 :rowNumber="true"
28 30 :rowSelection="true"
29 31 :toolbar="true"
... ... @@ -52,6 +54,8 @@
52 54 // Emits声明
53 55 const emit = defineEmits(['register','success']);
54 56 const isUpdate = ref(true);
  57 + const formDisabled = ref(false);
  58 + const modalRef = ref();
55 59 const refKeys = ref(['${tableVo.entityName?uncap_first}',<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
56 60 <#assign hasOne2Many = false>
57 61 <#assign hasOne2One = false>
... ... @@ -81,6 +85,7 @@
81 85 labelWidth: 150,
82 86 schemas: formSchema,
83 87 showActionButtonGroup: false,
  88 + baseColProps: {span: ${getFormSpan(tableVo.fieldRowNum?default(1))}}
84 89 });
85 90 //表单赋值
86 91 const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
... ... @@ -88,6 +93,7 @@
88 93 await reset();
89 94 setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
90 95 isUpdate.value = !!data?.isUpdate;
  96 + formDisabled.value = !data?.showFooter;
91 97 if (unref(isUpdate)) {
92 98 //表单赋值
93 99 await setFieldsValue({
... ... @@ -109,9 +115,21 @@
109 115 });
110 116 //方法配置
111 117 const [handleChangeTabs,handleSubmit,requestSubTableData,formRef] = useJvxeMethod(requestAddOrEdit,classifyIntoFormData,tableRefs,activeKey,refKeys<#if hasOne2One==true>,validateSubForm</#if>);
112   -
113   - //设置标题
114   - const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  118 + // 弹窗tabs滚动区域的高度
  119 + const tabsStyle = computed(() => {
  120 + let height: Nullable<string> = null
  121 + let minHeight = '100px'
  122 + let maxHeight: Nullable<string> = '500px'
  123 + // 弹窗wrapper
  124 + let modalWrapperRef = modalRef.value?.modalWrapperRef
  125 + if (modalWrapperRef) {
  126 + if (modalWrapperRef.fullScreen) {
  127 + height = 'calc(' + modalWrapperRef.spinStyle.height + ' - 50px)';
  128 + maxHeight = null
  129 + }
  130 + }
  131 + return {height, minHeight, maxHeight}
  132 + })
115 133  
116 134 async function reset(){
117 135 await resetFields();
... ... @@ -180,5 +198,38 @@
180 198 </script>
181 199  
182 200 <style lang="less" scoped>
  201 +/** 时间和数字输入框样式 */
  202 + :deep(.ant-input-number){
  203 + width: 100%
  204 + }
  205 +
  206 + :deep(.ant-calendar-picker){
  207 + width: 100%
  208 + }
  209 +</style>
  210 +
  211 +<style lang="less">
  212 +// Online表单Tab风格专属样式
  213 +.j-cgform-tab-modal {
  214 + .ant-modal-header {
  215 + padding-top: 8px;
  216 + padding-bottom: 8px;
  217 + border-bottom: none !important;
  218 + }
  219 +
  220 + .ant-modal .ant-modal-body > .scrollbar,
  221 + .ant-tabs-nav .ant-tabs-tab {
  222 + padding-top: 0;
  223 + }
  224 +
  225 + .ant-tabs-top-bar {
  226 + width: calc(100% - 55px);
  227 + position: relative;
  228 + left: -14px;
  229 + }
183 230  
  231 + .ant-tabs .ant-tabs-top-content > .ant-tabs-tabpane {
  232 + overflow: hidden auto;
  233 + }
  234 +}
184 235 </style>
185 236 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/[1-n]Form.vuei
  1 +<#include "/common/utils.ftl">
1 2 <#list subTables as sub>
2 3 <#if sub.foreignRelationType=='1'>
3 4 #segment#${sub.entityName}Form.vue
... ... @@ -15,11 +16,18 @@
15 16 name:"${sub.entityName}Form",
16 17 components: {BasicForm},
17 18 emits:['register'],
18   - setup(_,{emit}) {
19   - const [registerForm, {resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
  19 + props:{
  20 + disabled: {
  21 + type: Boolean,
  22 + default: false
  23 + }
  24 + },
  25 + setup(props,{emit}) {
  26 + const [registerForm, {setProps, resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
20 27 labelWidth: 150,
21 28 schemas: ${sub.entityName?uncap_first}FormSchema,
22 29 showActionButtonGroup: false,
  30 + baseColProps: {span: ${getFormSpan(tableVo.fieldRowNum?default(1))}}
23 31 });
24 32 /**
25 33 *初始化加载数据
... ... @@ -30,12 +38,19 @@
30 38 res.success && setFieldsValue({...res.result[0]});
31 39 })
32 40 }
  41 + setProps({disabled: props.disabled})
33 42 }
34 43 /**
35 44 *获取表单数据
36 45 */
37 46 function getFormData(){
38   - return [getFieldsValue()];
  47 + let formData = getFieldsValue();
  48 + Object.keys(formData).map(k=>{
  49 + if(formData[k] instanceof Array){
  50 + formData[k] = formData[k].join(',')
  51 + }
  52 + });
  53 + return [formData];
39 54 }
40 55 /**
41 56 *表单校验
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi
... ... @@ -78,11 +78,11 @@ export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
78 78 {
79 79 title: '${col.filedComment}',
80 80 key: '${col.fieldName}',
81   -<#if col.classType =='date'>
  81 +<#if col.fieldType =='date'>
82 82 type: JVxeTypes.date,
83   -<#elseif col.classType =='datetime'>
  83 +<#elseif col.fieldType =='datetime'>
84 84 type: JVxeTypes.datetime,
85   -<#elseif "int,decimal,double,"?contains(col.classType)>
  85 +<#elseif "int,decimal,double,"?contains(col.fieldType)>
86 86 type: JVxeTypes.inputNumber,
87 87 <#else>
88 88 type: JVxeTypes.input,
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi
... ... @@ -103,7 +103,11 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
103 103 <#if po.fieldType =='date'>
104 104 component: 'DatePicker',
105 105 <#elseif po.fieldType =='datetime'>
106   - component: 'TimePicker',
  106 + component: 'DatePicker',
  107 + componentProps: {
  108 + showTime: true,
  109 + valueFormat: 'YYYY-MM-DD hh:mm:ss',
  110 + },
107 111 <#elseif "int,decimal,double,"?contains(po.fieldType)>
108 112 component: 'InputNumber',
109 113 <#else>
... ...