feat(generator): 代码生成字段配置支持选择关联字典

暂时仅支持前端部分生成

Closes #I9SWQB
Closes #55
This commit is contained in:
2024-08-27 00:01:57 +08:00
parent e17d5db0fb
commit fdd21a01c1
17 changed files with 143 additions and 93 deletions

View File

@@ -141,6 +141,12 @@ public class FieldConfigDO implements Serializable {
@Schema(description = "查询方式", example = "1") @Schema(description = "查询方式", example = "1")
private QueryTypeEnum queryType; private QueryTypeEnum queryType;
/**
* 字典编码
*/
@Schema(description = "字典编码", example = "notice_type")
private String dictCode;
/** /**
* 创建时间 * 创建时间
*/ */

View File

@@ -366,27 +366,35 @@ public class GeneratorServiceImpl implements GeneratorService {
.toList(); .toList();
genConfigMap.put("fieldConfigs", fieldConfigList); genConfigMap.put("fieldConfigs", fieldConfigList);
// 统计部分特殊字段特征 // 统计部分特殊字段特征
genConfigMap.put("hasLocalDateTime", false); genConfigMap.put("hasLocalDateTimeField", false);
genConfigMap.put("hasBigDecimal", false); genConfigMap.put("hasBigDecimalField", false);
genConfigMap.put("hasRequiredField", false); genConfigMap.put("hasRequiredField", false);
genConfigMap.put("hasListQueryField", false); genConfigMap.put("hasListField", false);
Set<String> dictCodeSet = new HashSet<>();
for (FieldConfigDO fieldConfig : fieldConfigList) { for (FieldConfigDO fieldConfig : fieldConfigList) {
String fieldType = fieldConfig.getFieldType(); String fieldType = fieldConfig.getFieldType();
if ("LocalDateTime".equals(fieldType)) { if ("LocalDateTime".equals(fieldType)) {
genConfigMap.put("hasLocalDateTime", true); genConfigMap.put("hasLocalDateTimeField", true);
} }
if ("BigDecimal".equals(fieldType)) { if ("BigDecimal".equals(fieldType)) {
genConfigMap.put("hasBigDecimal", true); genConfigMap.put("hasBigDecimalField", true);
} }
// 必填项
if (Boolean.TRUE.equals(fieldConfig.getIsRequired())) { if (Boolean.TRUE.equals(fieldConfig.getIsRequired())) {
genConfigMap.put("hasRequiredField", true); genConfigMap.put("hasRequiredField", true);
} }
// 字典码
if (StrUtil.isNotBlank(fieldConfig.getDictCode())) {
genConfigMap.put("hasDictField", true);
dictCodeSet.add(fieldConfig.getDictCode());
}
QueryTypeEnum queryType = fieldConfig.getQueryType(); QueryTypeEnum queryType = fieldConfig.getQueryType();
if (null != queryType && StrUtil.equalsAny(queryType.name(), QueryTypeEnum.IN.name(), QueryTypeEnum.NOT_IN if (null != queryType && StrUtil.equalsAny(queryType.name(), QueryTypeEnum.IN.name(), QueryTypeEnum.NOT_IN
.name(), QueryTypeEnum.BETWEEN.name())) { .name(), QueryTypeEnum.BETWEEN.name())) {
genConfigMap.put("hasListQueryField", true); genConfigMap.put("hasListField", true);
} }
} }
genConfigMap.put("dictCodes", dictCodeSet);
String subPackageName = templateConfig.getPackageName(); String subPackageName = templateConfig.getPackageName();
genConfigMap.put("subPackageName", subPackageName); genConfigMap.put("subPackageName", subPackageName);
} }

View File

@@ -1,10 +1,10 @@
package ${packageName}.${subPackageName}; package ${packageName}.${subPackageName};
import java.io.Serial; import java.io.Serial;
<#if hasLocalDateTime> <#if hasLocalDateTimeField>
import java.time.LocalDateTime; import java.time.LocalDateTime;
</#if> </#if>
<#if hasBigDecimal> <#if hasBigDecimalField>
import java.math.BigDecimal; import java.math.BigDecimal;
</#if> </#if>

View File

@@ -1,10 +1,10 @@
package ${packageName}.${subPackageName}; package ${packageName}.${subPackageName};
import java.io.Serial; import java.io.Serial;
<#if hasLocalDateTime> <#if hasLocalDateTimeField>
import java.time.LocalDateTime; import java.time.LocalDateTime;
</#if> </#if>
<#if hasBigDecimal> <#if hasBigDecimalField>
import java.math.BigDecimal; import java.math.BigDecimal;
</#if> </#if>

View File

@@ -2,13 +2,13 @@ package ${packageName}.${subPackageName};
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
<#if hasLocalDateTime> <#if hasLocalDateTimeField>
import java.time.LocalDateTime; import java.time.LocalDateTime;
</#if> </#if>
<#if hasBigDecimal> <#if hasBigDecimalField>
import java.math.BigDecimal; import java.math.BigDecimal;
</#if> </#if>
<#if hasListQueryField> <#if hasListField>
import java.util.List; import java.util.List;
</#if> </#if>

View File

@@ -1,10 +1,10 @@
package ${packageName}.${subPackageName}; package ${packageName}.${subPackageName};
import java.io.Serial; import java.io.Serial;
<#if hasLocalDateTime> <#if hasLocalDateTimeField>
import java.time.LocalDateTime; import java.time.LocalDateTime;
</#if> </#if>
<#if hasBigDecimal> <#if hasBigDecimalField>
import java.math.BigDecimal; import java.math.BigDecimal;
</#if> </#if>

View File

@@ -1,10 +1,10 @@
package ${packageName}.${subPackageName}; package ${packageName}.${subPackageName};
import java.io.Serial; import java.io.Serial;
<#if hasLocalDateTime> <#if hasLocalDateTimeField>
import java.time.LocalDateTime; import java.time.LocalDateTime;
</#if> </#if>
<#if hasBigDecimal> <#if hasBigDecimalField>
import java.math.BigDecimal; import java.math.BigDecimal;
</#if> </#if>

View File

@@ -29,15 +29,9 @@ const isUpdate = computed(() => !!dataId.value)
const title = computed(() => (isUpdate.value ? '修改${businessName}' : '新增${businessName}')) const title = computed(() => (isUpdate.value ? '修改${businessName}' : '新增${businessName}'))
const formRef = ref<InstanceType<typeof GiForm>>() const formRef = ref<InstanceType<typeof GiForm>>()
<#list fieldConfigs as fieldConfig> <#if hasDictField>
<#if fieldConfig.showInForm> const { <#list dictCodes as dictCode>${dictCode}<#if dictCode_has_next>,</#if></#list> } = useDict(<#list dictCodes as dictCode>'${dictCode}'<#if dictCode_has_next>,</#if></#list>)
<#-- SELECT/RADIO/CHECK_BOX/TREE_SELECT控件从服务器端获取数据 -->
<#if fieldConfig.formType = 'SELECT' || fieldConfig.formType = 'RADIO'
|| fieldConfig.formType = 'CHECK_BOX' || fieldConfig.formType = 'TREE_SELECT'>
const { ${fieldConfig.columnName}_enum } = useDict('${fieldConfig.columnName}_enum')
</#if> </#if>
</#if>
</#list>
const options: Options = { const options: Options = {
form: {}, form: {},
@@ -67,16 +61,15 @@ const columns: Columns = reactive([
type: 'switch', type: 'switch',
<#elseif fieldConfig.formType = 'CHECK_BOX'> <#elseif fieldConfig.formType = 'CHECK_BOX'>
type: 'check-group', type: 'check-group',
options: ${fieldConfig.columnName}_enum,
<#elseif fieldConfig.formType = 'TREE_SELECT'> <#elseif fieldConfig.formType = 'TREE_SELECT'>
type: 'tree-select', type: 'tree-select',
data: '${fieldConfig.columnName}_enum',
<#elseif fieldConfig.formType = 'SELECT'> <#elseif fieldConfig.formType = 'SELECT'>
type: 'select', type: 'select',
options: ${fieldConfig.columnName}_enum,
<#elseif fieldConfig.formType = 'RADIO'> <#elseif fieldConfig.formType = 'RADIO'>
type: 'radio-group', type: 'radio-group'
options: ${fieldConfig.columnName}_enum, </#if>
<#if fieldConfig.dictCode?? && fieldConfig.dictCode != ''>
options: ${fieldConfig.dictCode},
</#if> </#if>
<#if fieldConfig.isRequired> <#if fieldConfig.isRequired>
rules: [{ required: true, message: '请输入${fieldConfig.comment}' }] rules: [{ required: true, message: '请输入${fieldConfig.comment}' }]

View File

@@ -12,62 +12,51 @@
:disabled-column-keys="['name']" :disabled-column-keys="['name']"
@refresh="search" @refresh="search"
> >
<#-- 查询字段配置 -->
<template #custom-left> <template #custom-left>
<#list fieldConfigs as fieldConfig> <#list fieldConfigs as fieldConfig>
<#if fieldConfig.showInQuery> <#if fieldConfig.showInQuery>
<#if fieldConfig.formType == "SELECT"><#-- 下拉框 --> <#if fieldConfig.formType == "SELECT"><#-- 下拉框 -->
<a-select <a-select
v-model="queryForm.${fieldConfig.fieldName}" v-model="queryForm.${fieldConfig.fieldName}"
:options="${fieldConfig.columnName}_enum" :options="${fieldConfig.columnName}_enum"
placeholder="请选择${fieldConfig.comment}" placeholder="请选择${fieldConfig.comment}"
allow-clear allow-clear
style="width: 150px" style="width: 150px"
@change="search" @change="search"
/> />
<#elseif fieldConfig.formType == "RADIO"><#-- 单选框 --> <#elseif fieldConfig.formType == "RADIO"><#-- 单选框 -->
<a-radio-group v-model="queryForm.${fieldConfig.fieldName}" :options="${fieldConfig.columnName}_enum" @change="search"/> <a-radio-group v-model="queryForm.${fieldConfig.fieldName}" :options="${fieldConfig.dictCode}" @change="search"/>
<#elseif fieldConfig.formType == "DATE"><#-- 日期框 --> <#elseif fieldConfig.formType == "DATE"><#-- 日期框 -->
<#if fieldConfig.queryType == "BETWEEN"> <#if fieldConfig.queryType == "BETWEEN">
<a-range-picker <DateRangePicker v-model="queryForm.${fieldConfig.fieldName}" format="YYYY-MM-DD" @change="search" />
v-model="queryForm.${fieldConfig.fieldName}" <#else>
:placeholder="['请选择开始${fieldConfig.comment}','请选择结束${fieldConfig.comment}']" <a-date-picker
format="YYYY-MM-DD" v-model="queryForm.${fieldConfig.fieldName}"
style="width: 100%" placeholder="请选择${fieldConfig.comment}"
/> format="YYYY-MM-DD"
<#else> style="height: 32px"
<a-date-picker />
v-model="queryForm.${fieldConfig.fieldName}"
placeholder="请选择${fieldConfig.comment}"
format="YYYY-MM-DD"
style="width: 100%"
/>
</#if>
<#elseif fieldConfig.formType == "DATE_TIME"><#-- 日期时间框 -->
<#if fieldConfig.queryType == "BETWEEN">
<a-range-picker
v-model="queryForm.${fieldConfig.fieldName}"
:placeholder="['请选择开始${fieldConfig.comment}','请选择结束${fieldConfig.comment}']"
show-time
format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
/>
<#else>
<a-date-picker
v-model="queryForm.${fieldConfig.fieldName}"
placeholder="请选择${fieldConfig.comment}"
show-time
format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
/>
</#if>
<#else>
<a-input v-model="queryForm.${fieldConfig.fieldName}" placeholder="请输入${fieldConfig.comment}" allow-clear @change="search">
<template #prefix><icon-search /></template>
</a-input>
</#if>
</#if> </#if>
</#list> <#elseif fieldConfig.formType == "DATE_TIME"><#-- 日期时间框 -->
<#if fieldConfig.queryType == "BETWEEN">
<DateRangePicker v-model="queryForm.${fieldConfig.fieldName}" @change="search" />
<#else>
<a-date-picker
v-model="queryForm.${fieldConfig.fieldName}"
placeholder="请选择${fieldConfig.comment}"
show-time
format="YYYY-MM-DD HH:mm:ss"
style="height: 32px"
/>
</#if>
<#else>
<a-input v-model="queryForm.${fieldConfig.fieldName}" placeholder="请输入${fieldConfig.comment}" allow-clear @change="search">
<template #prefix><icon-search /></template>
</a-input>
</#if>
</#if>
</#list>
<a-button @click="reset">重置</a-button> <a-button @click="reset">重置</a-button>
</template> </template>
<template #custom-right> <template #custom-right>
@@ -83,9 +72,19 @@
</a-button> </a-button>
</a-tooltip> </a-tooltip>
</template> </template>
<#-- 列字段配置 -->
<template #name="{ record }"> <template #name="{ record }">
<a-link @click="onDetail(record)">{{ record.name }}</a-link> <a-link @click="onDetail(record)">{{ record.name }}</a-link>
</template> </template>
<#list fieldConfigs as fieldConfig>
<#if fieldConfig.showInList>
<#if fieldConfig.dictCode?? && fieldConfig.dictCode != "">
<template #${fieldConfig.fieldName}="{ record }">
<GiCellTag :value="record.${fieldConfig.fieldName}" :dict="${fieldConfig.dictCode}" />
</template>
</#if>
</#if>
</#list>
<template #action="{ record }"> <template #action="{ record }">
<a-space> <a-space>
<a-link v-permission="['${apiModuleName}:${apiName}:update']" @click="onUpdate(record)">修改</a-link> <a-link v-permission="['${apiModuleName}:${apiName}:update']" @click="onUpdate(record)">修改</a-link>
@@ -118,6 +117,10 @@ import { useDict } from '@/hooks/app'
defineOptions({ name: '${classNamePrefix}' }) defineOptions({ name: '${classNamePrefix}' })
<#if hasDictField>
const { <#list dictCodes as dictCode>${dictCode}<#if dictCode_has_next>,</#if></#list> } = useDict(<#list dictCodes as dictCode>'${dictCode}'<#if dictCode_has_next>,</#if></#list>)
</#if>
const queryForm = reactive<${classNamePrefix}Query>({ const queryForm = reactive<${classNamePrefix}Query>({
<#list fieldConfigs as fieldConfig> <#list fieldConfigs as fieldConfig>
<#if fieldConfig.showInQuery> <#if fieldConfig.showInQuery>
@@ -127,14 +130,6 @@ const queryForm = reactive<${classNamePrefix}Query>({
sort: ['createTime,desc'] sort: ['createTime,desc']
}) })
<#list fieldConfigs as fieldConfig>
<#if fieldConfig.showInQuery>
<#if fieldConfig.formType == "SELECT" || fieldConfig.formType == "RADIO">
const { ${fieldConfig.columnName}_enum } = useDict('${fieldConfig.columnName}_enum')
</#if>
</#if>
</#list>
const { const {
tableData: dataList, tableData: dataList,
loading, loading,

View File

@@ -18,6 +18,7 @@ package top.continew.admin.system.model.entity;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import top.continew.starter.extension.crud.annotation.DictField;
import top.continew.starter.extension.crud.model.entity.BaseDO; import top.continew.starter.extension.crud.model.entity.BaseDO;
import java.io.Serial; import java.io.Serial;
@@ -29,6 +30,7 @@ import java.io.Serial;
* @since 2023/9/11 21:29 * @since 2023/9/11 21:29
*/ */
@Data @Data
@DictField(valueKey = "code")
@TableName("sys_dict") @TableName("sys_dict")
public class DictDO extends BaseDO { public class DictDO extends BaseDO {

View File

@@ -48,4 +48,11 @@ public interface DictItemService extends BaseService<DictItemResp, DictItemResp,
* @param dictIds 字典 ID 列表 * @param dictIds 字典 ID 列表
*/ */
void deleteByDictIds(List<Long> dictIds); void deleteByDictIds(List<Long> dictIds);
/**
* 查询枚举字典名称列表
*
* @return 枚举字典名称列表
*/
List<String> listEnumDictNames();
} }

View File

@@ -21,8 +21,11 @@ import top.continew.admin.system.model.query.DictQuery;
import top.continew.admin.system.model.req.DictReq; import top.continew.admin.system.model.req.DictReq;
import top.continew.admin.system.model.resp.DictResp; import top.continew.admin.system.model.resp.DictResp;
import top.continew.starter.data.mybatis.plus.service.IService; import top.continew.starter.data.mybatis.plus.service.IService;
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
import top.continew.starter.extension.crud.service.BaseService; import top.continew.starter.extension.crud.service.BaseService;
import java.util.List;
/** /**
* 字典业务接口 * 字典业务接口
* *
@@ -30,4 +33,11 @@ import top.continew.starter.extension.crud.service.BaseService;
* @since 2023/9/11 21:29 * @since 2023/9/11 21:29
*/ */
public interface DictService extends BaseService<DictResp, DictResp, DictQuery, DictReq>, IService<DictDO> { public interface DictService extends BaseService<DictResp, DictResp, DictQuery, DictReq>, IService<DictDO> {
/**
* 查询枚举字典
*
* @return 枚举字典列表
*/
List<LabelValueResp> listEnumDict();
} }

View File

@@ -32,8 +32,8 @@ import top.continew.admin.system.service.DictItemService;
import top.continew.starter.cache.redisson.util.RedisUtils; import top.continew.starter.cache.redisson.util.RedisUtils;
import top.continew.starter.core.autoconfigure.project.ProjectProperties; import top.continew.starter.core.autoconfigure.project.ProjectProperties;
import top.continew.starter.core.constant.StringConstants; import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.validate.CheckUtils;
import top.continew.starter.core.enums.BaseEnum; import top.continew.starter.core.enums.BaseEnum;
import top.continew.starter.core.util.validate.CheckUtils;
import top.continew.starter.extension.crud.model.resp.LabelValueResp; import top.continew.starter.extension.crud.model.resp.LabelValueResp;
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
@@ -81,6 +81,11 @@ public class DictItemServiceImpl extends BaseServiceImpl<DictItemMapper, DictIte
RedisUtils.deleteByPattern(CacheConstants.DICT_KEY_PREFIX + StringConstants.ASTERISK); RedisUtils.deleteByPattern(CacheConstants.DICT_KEY_PREFIX + StringConstants.ASTERISK);
} }
@Override
public List<String> listEnumDictNames() {
return ENUM_DICT_CACHE.keySet().stream().toList();
}
/** /**
* 字典值是否存在 * 字典值是否存在
* *

View File

@@ -26,6 +26,7 @@ import top.continew.admin.system.model.resp.DictResp;
import top.continew.admin.system.service.DictItemService; import top.continew.admin.system.service.DictItemService;
import top.continew.admin.system.service.DictService; import top.continew.admin.system.service.DictService;
import top.continew.starter.core.util.validate.CheckUtils; import top.continew.starter.core.util.validate.CheckUtils;
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
import java.util.List; import java.util.List;
@@ -71,6 +72,12 @@ public class DictServiceImpl extends BaseServiceImpl<DictMapper, DictDO, DictRes
dictItemService.deleteByDictIds(ids); dictItemService.deleteByDictIds(ids);
} }
@Override
public List<LabelValueResp> listEnumDict() {
List<String> enumDictNameList = dictItemService.listEnumDictNames();
return enumDictNameList.stream().map(name -> new LabelValueResp(name, name)).toList();
}
/** /**
* 名称是否存在 * 名称是否存在
* *

View File

@@ -32,7 +32,9 @@ import top.continew.admin.generator.model.req.GenConfigReq;
import top.continew.admin.generator.model.resp.GeneratePreviewResp; import top.continew.admin.generator.model.resp.GeneratePreviewResp;
import top.continew.admin.generator.model.resp.TableResp; import top.continew.admin.generator.model.resp.TableResp;
import top.continew.admin.generator.service.GeneratorService; import top.continew.admin.generator.service.GeneratorService;
import top.continew.admin.system.service.DictService;
import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.query.PageQuery;
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
import top.continew.starter.extension.crud.model.resp.PageResp; import top.continew.starter.extension.crud.model.resp.PageResp;
import java.sql.SQLException; import java.sql.SQLException;
@@ -52,6 +54,7 @@ import java.util.List;
public class GeneratorController { public class GeneratorController {
private final GeneratorService baseService; private final GeneratorService baseService;
private final DictService dictService;
@Operation(summary = "分页查询数据表", description = "分页查询数据表") @Operation(summary = "分页查询数据表", description = "分页查询数据表")
@SaCheckPermission("tool:generator:list") @SaCheckPermission("tool:generator:list")
@@ -101,4 +104,13 @@ public class GeneratorController {
public void generate(@PathVariable List<String> tableNames, HttpServletResponse response) { public void generate(@PathVariable List<String> tableNames, HttpServletResponse response) {
baseService.generate(tableNames, response); baseService.generate(tableNames, response);
} }
@Operation(summary = "查询字典", description = "查询字典列表")
@SaCheckPermission("tool:generator:list")
@GetMapping("/dict")
public List<LabelValueResp> listDict() {
List<LabelValueResp> dictList = dictService.listDict(null, null);
dictList.addAll(dictService.listEnumDict());
return dictList;
}
} }

View File

@@ -1,2 +1,4 @@
-- liquibase formatted sql -- liquibase formatted sql
-- changeset Charles7c:3.3-1
ALTER TABLE `gen_field_config` ADD COLUMN `dict_code` varchar(30) DEFAULT NULL COMMENT '字典编码' AFTER `query_type`;

View File

@@ -1,2 +1,5 @@
-- liquibase formatted sql -- liquibase formatted sql
-- changeset Charles7c:3.3-1
ALTER TABLE "gen_field_config" ADD COLUMN `dict_code` varchar(30) DEFAULT NULL COMMENT '字典编码' AFTER `query_type`;
COMMENT ON COLUMN "gen_field_config"."dict_code" IS '字典编码';