feat(cms): 添加 CMS 模块控制器
- 新增 CmsAdController、CmsAdRecordController、CmsArticleCategoryController、CmsArticleCommentController、CmsArticleContentController、CmsArticleController 和 CmsArticleCountController - 实现了广告位、广告图片、文章分类、文章评论、文章记录、文章和点赞文章的 CRUD操作 - 添加了分页查询、批量操作等接口 -集成了 Swagger 文档注解 - 优化了代码结构,提高了可维护性
This commit is contained in:
@@ -1,406 +0,0 @@
|
||||
package com.gxwebsoft.generator;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
import com.baomidou.mybatisplus.generator.AutoGenerator;
|
||||
import com.baomidou.mybatisplus.generator.InjectionConfig;
|
||||
import com.baomidou.mybatisplus.generator.config.*;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
||||
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
|
||||
import com.gxwebsoft.generator.engine.BeetlTemplateEnginePlus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* CMS模块-代码生成工具
|
||||
*
|
||||
* @author WebSoft
|
||||
* @since 2021-09-05 00:31:14
|
||||
*/
|
||||
public class CmsGenerator {
|
||||
// 输出位置
|
||||
private static final String OUTPUT_LOCATION = System.getProperty("user.dir");
|
||||
//private static final String OUTPUT_LOCATION = "D:/codegen"; // 不想生成到项目中可以写磁盘路径
|
||||
// 输出目录
|
||||
private static final String OUTPUT_DIR = "/src/main/java";
|
||||
// Vue文件输出位置
|
||||
private static final String OUTPUT_LOCATION_VUE = "/Users/gxwebsoft/VUE/site";
|
||||
// UniApp文件输出目录
|
||||
private static final String OUTPUT_LOCATION_UNIAPP = "/Users/gxwebsoft/VUE/template-10550";
|
||||
// Vue文件输出目录
|
||||
private static final String OUTPUT_DIR_VUE = "/src";
|
||||
// 作者名称
|
||||
private static final String AUTHOR = "科技小王子";
|
||||
// 是否在xml中添加二级缓存配置
|
||||
private static final boolean ENABLE_CACHE = false;
|
||||
// 数据库连接配置
|
||||
private static final String DB_URL = "jdbc:mysql://47.119.165.234:3308/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8";
|
||||
private static final String DB_DRIVER = "com.mysql.cj.jdbc.Driver";
|
||||
private static final String DB_USERNAME = "modules";
|
||||
private static final String DB_PASSWORD = "8YdLnk7KsPAyDXGA";
|
||||
// 包名
|
||||
private static final String PACKAGE_NAME = "com.gxwebsoft";
|
||||
// 模块名
|
||||
private static final String MODULE_NAME = "cms";
|
||||
// 需要生成的表
|
||||
private static final String[] TABLE_NAMES = new String[]{
|
||||
// "cms_article",
|
||||
// "cms_article_category",
|
||||
// "cms_article_like",
|
||||
// "cms_article_comment",
|
||||
// "cms_article_content",
|
||||
// "cms_docs",
|
||||
// "cms_docs_book",
|
||||
// "cms_docs_content",
|
||||
// "cms_ad",
|
||||
// "cms_ad_record",
|
||||
// "cms_navigation",
|
||||
// "cms_design",
|
||||
// "cms_design_record",
|
||||
// "cms_website",
|
||||
// "cms_website_field",
|
||||
// "cms_form",
|
||||
// "cms_form_record",
|
||||
// "cms_domain",
|
||||
// "cms_mp_menu"
|
||||
// "cms_mp_pages",
|
||||
// "cms_mp"
|
||||
// "cms_mp_field"
|
||||
// "cms_mp_ad"
|
||||
// "cms_components"
|
||||
// "cms_mp_official_menu",
|
||||
// "cms_order",
|
||||
// "cms_model"
|
||||
// "cms_lang",
|
||||
// "cms_lang_log",
|
||||
// "cms_website_setting",
|
||||
// "cms_statistics"
|
||||
|
||||
};
|
||||
// 需要去除的表前缀
|
||||
private static final String[] TABLE_PREFIX = new String[]{
|
||||
"tb_"
|
||||
};
|
||||
// 不需要作为查询参数的字段
|
||||
private static final String[] PARAM_EXCLUDE_FIELDS = new String[]{
|
||||
"tenant_id",
|
||||
"create_time",
|
||||
"update_time"
|
||||
};
|
||||
// 查询参数使用String的类型
|
||||
private static final String[] PARAM_TO_STRING_TYPE = new String[]{
|
||||
"Date",
|
||||
"LocalDate",
|
||||
"LocalTime",
|
||||
"LocalDateTime"
|
||||
};
|
||||
// 查询参数使用EQ的类型
|
||||
private static final String[] PARAM_EQ_TYPE = new String[]{
|
||||
"Integer",
|
||||
"Boolean",
|
||||
"BigDecimal"
|
||||
};
|
||||
// 是否添加权限注解
|
||||
private static final boolean AUTH_ANNOTATION = true;
|
||||
// 是否添加日志注解
|
||||
private static final boolean LOG_ANNOTATION = true;
|
||||
// controller的mapping前缀
|
||||
private static final String CONTROLLER_MAPPING_PREFIX = "/api";
|
||||
// 模板所在位置
|
||||
private static final String TEMPLATES_DIR = "/src/test/java/com/gxwebsoft/generator/templates";
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 代码生成器
|
||||
AutoGenerator mpg = new AutoGenerator();
|
||||
|
||||
// 全局配置
|
||||
GlobalConfig gc = new GlobalConfig();
|
||||
gc.setOutputDir(OUTPUT_LOCATION + OUTPUT_DIR);
|
||||
gc.setAuthor(AUTHOR);
|
||||
gc.setOpen(false);
|
||||
gc.setFileOverride(true);
|
||||
gc.setEnableCache(ENABLE_CACHE);
|
||||
gc.setSwagger2(true);
|
||||
gc.setIdType(IdType.AUTO);
|
||||
gc.setServiceName("%sService");
|
||||
mpg.setGlobalConfig(gc);
|
||||
|
||||
// 数据源配置
|
||||
DataSourceConfig dsc = new DataSourceConfig();
|
||||
dsc.setUrl(DB_URL);
|
||||
// dsc.setSchemaName("public");
|
||||
dsc.setDriverName(DB_DRIVER);
|
||||
dsc.setUsername(DB_USERNAME);
|
||||
dsc.setPassword(DB_PASSWORD);
|
||||
mpg.setDataSource(dsc);
|
||||
|
||||
// 包配置
|
||||
PackageConfig pc = new PackageConfig();
|
||||
pc.setModuleName(MODULE_NAME);
|
||||
pc.setParent(PACKAGE_NAME);
|
||||
mpg.setPackageInfo(pc);
|
||||
|
||||
// 策略配置
|
||||
StrategyConfig strategy = new StrategyConfig();
|
||||
strategy.setNaming(NamingStrategy.underline_to_camel);
|
||||
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
|
||||
strategy.setInclude(TABLE_NAMES);
|
||||
strategy.setTablePrefix(TABLE_PREFIX);
|
||||
strategy.setSuperControllerClass(PACKAGE_NAME + ".common.core.web.BaseController");
|
||||
strategy.setEntityLombokModel(true);
|
||||
strategy.setRestControllerStyle(true);
|
||||
strategy.setControllerMappingHyphenStyle(true);
|
||||
strategy.setLogicDeleteFieldName("deleted");
|
||||
mpg.setStrategy(strategy);
|
||||
|
||||
// 模板配置
|
||||
TemplateConfig templateConfig = new TemplateConfig();
|
||||
templateConfig.setController(TEMPLATES_DIR + "/controller.java");
|
||||
templateConfig.setEntity(TEMPLATES_DIR + "/entity.java");
|
||||
templateConfig.setMapper(TEMPLATES_DIR + "/mapper.java");
|
||||
templateConfig.setXml(TEMPLATES_DIR + "/mapper.xml");
|
||||
templateConfig.setService(TEMPLATES_DIR + "/service.java");
|
||||
templateConfig.setServiceImpl(TEMPLATES_DIR + "/serviceImpl.java");
|
||||
mpg.setTemplate(templateConfig);
|
||||
mpg.setTemplateEngine(new BeetlTemplateEnginePlus());
|
||||
|
||||
// 自定义模板配置
|
||||
InjectionConfig cfg = new InjectionConfig() {
|
||||
@Override
|
||||
public void initMap() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("packageName", PACKAGE_NAME);
|
||||
map.put("paramExcludeFields", PARAM_EXCLUDE_FIELDS);
|
||||
map.put("paramToStringType", PARAM_TO_STRING_TYPE);
|
||||
map.put("paramEqType", PARAM_EQ_TYPE);
|
||||
map.put("authAnnotation", AUTH_ANNOTATION);
|
||||
map.put("logAnnotation", LOG_ANNOTATION);
|
||||
map.put("controllerMappingPrefix", CONTROLLER_MAPPING_PREFIX);
|
||||
this.setMap(map);
|
||||
}
|
||||
};
|
||||
String templatePath = TEMPLATES_DIR + "/param.java.btl";
|
||||
List<FileOutConfig> focList = new ArrayList<>();
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION + OUTPUT_DIR + "/"
|
||||
+ PACKAGE_NAME.replace(".", "/")
|
||||
+ "/" + pc.getModuleName() + "/param/"
|
||||
+ tableInfo.getEntityName() + "Param" + StringPool.DOT_JAVA;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* 以下是生成VUE项目代码
|
||||
* 生成文件的路径 /api/shop/goods/index.ts
|
||||
*/
|
||||
templatePath = TEMPLATES_DIR + "/index.ts.btl";
|
||||
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_VUE + OUTPUT_DIR_VUE
|
||||
+ "/api/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/" + "index.ts";
|
||||
}
|
||||
});
|
||||
focList.add(new FileOutConfig() {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_UNIAPP + OUTPUT_DIR_VUE
|
||||
+ "/api/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/" + "index.ts";
|
||||
}
|
||||
});
|
||||
// 生成TS文件 (/api/shop/goods/model/index.ts)
|
||||
templatePath = TEMPLATES_DIR + "/model.ts.btl";
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_VUE + OUTPUT_DIR_VUE
|
||||
+ "/api/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/model/" + "index.ts";
|
||||
}
|
||||
});
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_UNIAPP + OUTPUT_DIR_VUE
|
||||
+ "/api/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/model/" + "index.ts";
|
||||
}
|
||||
});
|
||||
// 生成Vue文件(/views/shop/goods/index.vue)
|
||||
templatePath = TEMPLATES_DIR + "/index.vue.btl";
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_VUE + OUTPUT_DIR_VUE
|
||||
+ "/views/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/" + "index.vue";
|
||||
}
|
||||
});
|
||||
|
||||
// 生成components文件(/views/shop/goods/components/edit.vue)
|
||||
templatePath = TEMPLATES_DIR + "/components.edit.vue.btl";
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_VUE + OUTPUT_DIR_VUE
|
||||
+ "/views/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/components/" + tableInfo.getEntityPath() + "Edit.vue";
|
||||
}
|
||||
});
|
||||
|
||||
// 生成components文件(/views/shop/goods/components/search.vue)
|
||||
templatePath = TEMPLATES_DIR + "/components.search.vue.btl";
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_VUE + OUTPUT_DIR_VUE
|
||||
+ "/views/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/components/" + "search.vue";
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 移动端页面文件生成 ==========
|
||||
// 生成移动端列表页面配置文件 (/src/cms/article/index.config.ts)
|
||||
templatePath = TEMPLATES_DIR + "/index.config.ts.btl";
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_UNIAPP + OUTPUT_DIR_VUE
|
||||
+ "/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/" + "index.config.ts";
|
||||
}
|
||||
});
|
||||
|
||||
// 生成移动端列表页面组件文件 (/src/cms/article/index.tsx)
|
||||
templatePath = TEMPLATES_DIR + "/index.tsx.btl";
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_UNIAPP + OUTPUT_DIR_VUE
|
||||
+ "/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/" + "index.tsx";
|
||||
}
|
||||
});
|
||||
|
||||
// 生成移动端新增/编辑页面配置文件 (/src/cms/article/add.config.ts)
|
||||
templatePath = TEMPLATES_DIR + "/add.config.ts.btl";
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_UNIAPP + OUTPUT_DIR_VUE
|
||||
+ "/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/" + "add.config.ts";
|
||||
}
|
||||
});
|
||||
|
||||
// 生成移动端新增/编辑页面组件文件 (/src/cms/article/add.tsx)
|
||||
templatePath = TEMPLATES_DIR + "/add.tsx.btl";
|
||||
focList.add(new FileOutConfig(templatePath) {
|
||||
@Override
|
||||
public String outputFile(TableInfo tableInfo) {
|
||||
return OUTPUT_LOCATION_UNIAPP + OUTPUT_DIR_VUE
|
||||
+ "/" + pc.getModuleName() + "/"
|
||||
+ tableInfo.getEntityPath() + "/" + "add.tsx";
|
||||
}
|
||||
});
|
||||
|
||||
cfg.setFileOutConfigList(focList);
|
||||
mpg.setCfg(cfg);
|
||||
|
||||
mpg.execute();
|
||||
|
||||
// 自动更新 app.config.ts
|
||||
updateAppConfig(TABLE_NAMES, MODULE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动更新 app.config.ts 文件,添加新生成的页面路径
|
||||
*/
|
||||
private static void updateAppConfig(String[] tableNames, String moduleName) {
|
||||
String appConfigPath = OUTPUT_LOCATION_UNIAPP + OUTPUT_DIR_VUE + "/app.config.ts";
|
||||
|
||||
try {
|
||||
// 读取原文件内容
|
||||
String content = new String(Files.readAllBytes(Paths.get(appConfigPath)));
|
||||
|
||||
// 为每个表生成页面路径
|
||||
StringBuilder newPages = new StringBuilder();
|
||||
for (String tableName : tableNames) {
|
||||
String entityPath = tableName.replaceAll("_", "");
|
||||
// 转换为驼峰命名
|
||||
String[] parts = tableName.split("_");
|
||||
StringBuilder camelCase = new StringBuilder(parts[0]);
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
camelCase.append(parts[i].substring(0, 1).toUpperCase()).append(parts[i].substring(1));
|
||||
}
|
||||
entityPath = camelCase.toString();
|
||||
|
||||
newPages.append(" '").append(entityPath).append("/index',\n");
|
||||
newPages.append(" '").append(entityPath).append("/add',\n");
|
||||
}
|
||||
|
||||
// 查找对应模块的子包配置
|
||||
String modulePattern = "\"root\":\\s*\"" + moduleName + "\",\\s*\"pages\":\\s*\\[([^\\]]*)]";
|
||||
Pattern pattern = Pattern.compile(modulePattern, Pattern.DOTALL);
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
|
||||
if (matcher.find()) {
|
||||
String existingPages = matcher.group(1);
|
||||
|
||||
// 检查页面是否已存在,避免重复添加
|
||||
boolean needUpdate = false;
|
||||
String[] newPageArray = newPages.toString().split("\n");
|
||||
for (String newPage : newPageArray) {
|
||||
if (!newPage.trim().isEmpty() && !existingPages.contains(newPage.trim().replace(" ", "").replace(",", ""))) {
|
||||
needUpdate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needUpdate) {
|
||||
// 备份原文件
|
||||
String backupPath = appConfigPath + ".backup." + System.currentTimeMillis();
|
||||
Files.copy(Paths.get(appConfigPath), Paths.get(backupPath));
|
||||
System.out.println("已备份原文件到: " + backupPath);
|
||||
|
||||
// 在现有页面列表末尾添加新页面
|
||||
String updatedPages = existingPages.trim();
|
||||
if (!updatedPages.endsWith(",")) {
|
||||
updatedPages += ",";
|
||||
}
|
||||
updatedPages += "\n" + newPages.toString().trim();
|
||||
|
||||
// 替换内容
|
||||
String updatedContent = content.replace(matcher.group(1), updatedPages);
|
||||
|
||||
// 写入更新后的内容
|
||||
Files.write(Paths.get(appConfigPath), updatedContent.getBytes());
|
||||
|
||||
System.out.println("✅ 已自动更新 app.config.ts,添加了以下页面路径:");
|
||||
System.out.println(newPages.toString());
|
||||
} else {
|
||||
System.out.println("ℹ️ app.config.ts 中已包含所有页面路径,无需更新");
|
||||
}
|
||||
} else {
|
||||
System.out.println("⚠️ 未找到 " + moduleName + " 模块的子包配置,请手动添加页面路径");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ 更新 app.config.ts 失败: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
# 表格列生成规则优化方案
|
||||
|
||||
## 问题描述
|
||||
|
||||
原有的表格列生成规则会为所有字段(除了tenantId)生成列,导致:
|
||||
- 表格列过多,显示混乱
|
||||
- 重要信息被淹没在大量字段中
|
||||
- 用户体验差,需要横向滚动查看
|
||||
|
||||
## 优化方案
|
||||
|
||||
### 1. 使用 hideInTable 属性(推荐方案)
|
||||
|
||||
**核心思想**:生成所有字段的列配置,但使用 `hideInTable: true` 隐藏非核心字段
|
||||
|
||||
**优势**:
|
||||
- ✅ **保留完整信息**:所有字段都会生成,不丢失任何数据
|
||||
- ✅ **用户可自定义**:通过表格的列设置功能,用户可以勾选显示需要的字段
|
||||
- ✅ **默认简洁**:只显示最重要的核心字段,保持表格整洁
|
||||
- ✅ **灵活性强**:不同用户可以根据需要显示不同的列组合
|
||||
|
||||
**核心字段判断规则**:
|
||||
- 主键字段:始终显示
|
||||
- 名称/标题字段:始终显示
|
||||
- 编码字段:始终显示
|
||||
- 状态字段:始终显示
|
||||
- 排序字段:始终显示
|
||||
- 创建时间:始终显示
|
||||
- 其他字段:默认隐藏(`hideInTable: true`)
|
||||
|
||||
### 2. 排除字段
|
||||
|
||||
以下字段不会显示在表格中:
|
||||
- `tenantId` - 租户ID
|
||||
- `deleted` - 逻辑删除标记
|
||||
- `version` - 版本号
|
||||
- `remark` - 备注(内容通常较长)
|
||||
- `description` - 描述(内容通常较长)
|
||||
- `content` - 内容(内容通常较长)
|
||||
|
||||
### 3. 字段宽度优化
|
||||
|
||||
根据字段类型和内容特点设置合适的宽度:
|
||||
- ID字段:90px
|
||||
- 状态字段:80px
|
||||
- 时间字段:120px
|
||||
- 字符串字段:150px
|
||||
- 数值字段:120px
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 方法1:使用 hideInTable 模板(强烈推荐)
|
||||
|
||||
已经修改了 `index.vue.btl` 模板,现在会自动:
|
||||
1. **生成所有字段**:不丢失任何字段信息
|
||||
2. **智能隐藏**:非核心字段添加 `hideInTable: true`
|
||||
3. **用户可控**:用户可通过表格的列设置功能显示需要的字段
|
||||
|
||||
**生成的代码示例**:
|
||||
```javascript
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 90,
|
||||
// 核心字段,默认显示
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 150,
|
||||
// 核心字段,默认显示
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createBy',
|
||||
key: 'createBy',
|
||||
width: 120,
|
||||
hideInTable: true, // 非核心字段,默认隐藏
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
key: 'remark',
|
||||
width: 200,
|
||||
hideInTable: true, // 长文本字段,默认隐藏
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
### 方法2:前端列设置功能
|
||||
|
||||
参考 `table-with-column-settings.vue` 示例,可以添加列设置功能:
|
||||
|
||||
```javascript
|
||||
// 列可见性控制
|
||||
const allColumns = ref([
|
||||
{ title: 'ID', visible: true, required: true },
|
||||
{ title: '名称', visible: true, required: true },
|
||||
{ title: '创建人', visible: false, required: false },
|
||||
// ...
|
||||
]);
|
||||
|
||||
// 根据可见性过滤列
|
||||
const columns = computed(() => {
|
||||
return allColumns.value.filter(col => col.visible);
|
||||
});
|
||||
```
|
||||
|
||||
### 方法3:自定义核心字段规则
|
||||
|
||||
在模板中修改核心字段判断逻辑:
|
||||
|
||||
```javascript
|
||||
// 在模板中添加你的业务字段
|
||||
<% } else if(field.propertyName == 'yourBusinessField'){ %>
|
||||
// 设为核心字段,默认显示
|
||||
isCoreField = true;
|
||||
```
|
||||
|
||||
## 生成效果对比
|
||||
|
||||
### 优化前
|
||||
```
|
||||
| ID | 名称 | 编码 | 状态 | 排序 | 创建人 | 创建时间 | 更新人 | 更新时间 | 备注 | 描述 | ... | 操作 |
|
||||
```
|
||||
*显示所有字段,表格过宽,信息混乱*
|
||||
|
||||
### 优化后(使用 hideInTable)
|
||||
**默认显示**:
|
||||
```
|
||||
| ID | 名称 | 编码 | 状态 | 排序 | 创建时间 | 操作 |
|
||||
```
|
||||
*只显示核心字段,表格简洁*
|
||||
|
||||
**用户可通过列设置显示更多字段**:
|
||||
```
|
||||
| ID | 名称 | 编码 | 状态 | ✓创建人 | 创建时间 | ✓更新时间 | ✓备注 | 操作 |
|
||||
```
|
||||
*用户勾选后可显示需要的字段*
|
||||
|
||||
### 列设置界面示例
|
||||
```
|
||||
列设置
|
||||
☑ ID (固定显示)
|
||||
☑ 名称 (固定显示)
|
||||
☑ 编码 (固定显示)
|
||||
☑ 状态 (固定显示)
|
||||
☐ 创建人 (可选)
|
||||
☑ 创建时间 (固定显示)
|
||||
☐ 更新人 (可选)
|
||||
☐ 更新时间 (可选)
|
||||
☐ 备注 (可选)
|
||||
☐ 描述 (可选)
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 修改默认显示列数
|
||||
|
||||
在模板中修改:
|
||||
```javascript
|
||||
.slice(0, 6); // 改为你想要的列数
|
||||
```
|
||||
|
||||
### 添加新的优先字段
|
||||
|
||||
在模板的优先级判断中添加:
|
||||
```javascript
|
||||
<% } else if(field.propertyName == 'yourField'){ %>
|
||||
width: 120,
|
||||
priority: 8, // 设置优先级
|
||||
```
|
||||
|
||||
### 排除特定字段
|
||||
|
||||
在条件判断中添加:
|
||||
```javascript
|
||||
<% if(field.propertyName != 'tenantId' && field.propertyName != 'yourExcludedField'){ %>
|
||||
```
|
||||
|
||||
## 建议
|
||||
|
||||
1. **保持默认6列**:这是最佳的用户体验,既能显示关键信息,又不会过于拥挤
|
||||
2. **优先显示业务关键字段**:如名称、状态、创建时间等
|
||||
3. **提供列设置功能**:让用户可以自定义显示哪些列
|
||||
4. **响应式设计**:在移动端进一步减少显示列数
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 修改模板后需要重新生成代码才能生效
|
||||
- 建议在生成前备份原有模板文件
|
||||
- 可以根据具体业务需求调整优先级规则
|
||||
@@ -1,149 +0,0 @@
|
||||
package com.gxwebsoft.generator;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* 简单的测试生成器,用于验证模板文件的完整性
|
||||
*/
|
||||
public class SimpleTestGenerator {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== 代码生成器模板验证 ===");
|
||||
|
||||
String templatesDir = "src/test/java/com/gxwebsoft/generator/templates";
|
||||
|
||||
// 检查必要的模板文件
|
||||
String[] requiredTemplates = {
|
||||
"index.vue.btl",
|
||||
"components.edit.vue.btl",
|
||||
"components.search.vue.btl",
|
||||
"index.tsx.btl",
|
||||
"add.tsx.btl",
|
||||
"index.config.ts.btl",
|
||||
"add.config.ts.btl",
|
||||
"index.ts.uniapp.btl",
|
||||
"model.ts.uniapp.btl",
|
||||
"controller.java.btl",
|
||||
"service.java.btl",
|
||||
"serviceImpl.java.btl",
|
||||
"mapper.java.btl",
|
||||
"mapper.xml.btl",
|
||||
"entity.java.btl",
|
||||
"param.java.btl"
|
||||
};
|
||||
|
||||
boolean allTemplatesExist = true;
|
||||
|
||||
for (String template : requiredTemplates) {
|
||||
String filePath = templatesDir + "/" + template;
|
||||
File file = new File(filePath);
|
||||
|
||||
if (file.exists()) {
|
||||
try {
|
||||
long fileSize = Files.size(Paths.get(filePath));
|
||||
System.out.println("✅ " + template + " (大小: " + fileSize + " 字节)");
|
||||
} catch (Exception e) {
|
||||
System.out.println("⚠️ " + template + " (无法读取文件大小)");
|
||||
}
|
||||
} else {
|
||||
System.out.println("❌ " + template + " (文件不存在)");
|
||||
allTemplatesExist = false;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("\n=== 验证结果 ===");
|
||||
if (allTemplatesExist) {
|
||||
System.out.println("✅ 所有模板文件都存在且可读");
|
||||
|
||||
// 检查关键模板的内容完整性
|
||||
checkTemplateIntegrity();
|
||||
|
||||
} else {
|
||||
System.out.println("❌ 部分模板文件缺失");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkTemplateIntegrity() {
|
||||
System.out.println("\n=== 模板内容完整性检查 ===");
|
||||
|
||||
// 检查Vue模板
|
||||
checkVueTemplate();
|
||||
|
||||
// 检查移动端模板
|
||||
checkMobileTemplate();
|
||||
|
||||
// 检查API模板
|
||||
checkApiTemplate();
|
||||
}
|
||||
|
||||
private static void checkVueTemplate() {
|
||||
try {
|
||||
String content = new String(Files.readAllBytes(
|
||||
Paths.get("src/test/java/com/gxwebsoft/generator/templates/index.vue.btl")));
|
||||
|
||||
boolean hasTemplate = content.contains("<template>");
|
||||
boolean hasScript = content.contains("<script");
|
||||
boolean hasImports = content.contains("import");
|
||||
boolean hasExport = content.contains("export default");
|
||||
|
||||
System.out.println("Vue模板检查:");
|
||||
System.out.println(" - Template标签: " + (hasTemplate ? "✅" : "❌"));
|
||||
System.out.println(" - Script标签: " + (hasScript ? "✅" : "❌"));
|
||||
System.out.println(" - Import语句: " + (hasImports ? "✅" : "❌"));
|
||||
System.out.println(" - Export语句: " + (hasExport ? "✅" : "❌"));
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("Vue模板检查失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkMobileTemplate() {
|
||||
try {
|
||||
String content = new String(Files.readAllBytes(
|
||||
Paths.get("src/test/java/com/gxwebsoft/generator/templates/index.tsx.btl")));
|
||||
|
||||
boolean hasImports = content.contains("import");
|
||||
boolean hasComponent = content.contains("const") && content.contains("Manage");
|
||||
boolean hasExport = content.contains("export default");
|
||||
boolean hasSearchBar = content.contains("SearchBar");
|
||||
boolean hasInfiniteLoading = content.contains("InfiniteLoading");
|
||||
|
||||
System.out.println("移动端模板检查:");
|
||||
System.out.println(" - Import语句: " + (hasImports ? "✅" : "❌"));
|
||||
System.out.println(" - 组件定义: " + (hasComponent ? "✅" : "❌"));
|
||||
System.out.println(" - Export语句: " + (hasExport ? "✅" : "❌"));
|
||||
System.out.println(" - 搜索功能: " + (hasSearchBar ? "✅" : "❌"));
|
||||
System.out.println(" - 无限滚动: " + (hasInfiniteLoading ? "✅" : "❌"));
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("移动端模板检查失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkApiTemplate() {
|
||||
try {
|
||||
String content = new String(Files.readAllBytes(
|
||||
Paths.get("src/test/java/com/gxwebsoft/generator/templates/index.ts.uniapp.btl")));
|
||||
|
||||
boolean hasPageFunction = content.contains("export async function page");
|
||||
boolean hasListFunction = content.contains("export async function list");
|
||||
boolean hasAddFunction = content.contains("export async function add");
|
||||
boolean hasUpdateFunction = content.contains("export async function update");
|
||||
boolean hasRemoveFunction = content.contains("export async function remove");
|
||||
boolean hasGetFunction = content.contains("export async function get");
|
||||
|
||||
System.out.println("API模板检查:");
|
||||
System.out.println(" - 分页查询: " + (hasPageFunction ? "✅" : "❌"));
|
||||
System.out.println(" - 列表查询: " + (hasListFunction ? "✅" : "❌"));
|
||||
System.out.println(" - 添加功能: " + (hasAddFunction ? "✅" : "❌"));
|
||||
System.out.println(" - 更新功能: " + (hasUpdateFunction ? "✅" : "❌"));
|
||||
System.out.println(" - 删除功能: " + (hasRemoveFunction ? "✅" : "❌"));
|
||||
System.out.println(" - 详情查询: " + (hasGetFunction ? "✅" : "❌"));
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("API模板检查失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.gxwebsoft.generator;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.generator.AutoGenerator;
|
||||
import com.baomidou.mybatisplus.generator.config.*;
|
||||
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
|
||||
import com.gxwebsoft.generator.engine.BeetlTemplateEnginePlus;
|
||||
|
||||
/**
|
||||
* 测试代码生成器
|
||||
*
|
||||
* @author WebSoft
|
||||
* @since 2025-01-09
|
||||
*/
|
||||
public class TestGenerator {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== 测试 MyBatis-Plus 代码生成器 ===");
|
||||
|
||||
try {
|
||||
// 测试基本配置
|
||||
AutoGenerator mpg = new AutoGenerator();
|
||||
|
||||
// 全局配置
|
||||
GlobalConfig gc = new GlobalConfig();
|
||||
gc.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
|
||||
gc.setAuthor("WebSoft");
|
||||
gc.setOpen(false);
|
||||
gc.setFileOverride(false); // 设置为false避免覆盖现有文件
|
||||
gc.setSwagger2(true);
|
||||
gc.setIdType(IdType.AUTO);
|
||||
gc.setServiceName("%sService");
|
||||
mpg.setGlobalConfig(gc);
|
||||
|
||||
// 数据源配置(使用测试配置,不连接真实数据库)
|
||||
DataSourceConfig dsc = new DataSourceConfig();
|
||||
dsc.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
|
||||
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
|
||||
dsc.setUsername("test");
|
||||
dsc.setPassword("test");
|
||||
mpg.setDataSource(dsc);
|
||||
|
||||
// 包配置
|
||||
PackageConfig pc = new PackageConfig();
|
||||
pc.setModuleName("test");
|
||||
pc.setParent("com.gxwebsoft");
|
||||
mpg.setPackageInfo(pc);
|
||||
|
||||
// 策略配置
|
||||
StrategyConfig strategy = new StrategyConfig();
|
||||
strategy.setNaming(NamingStrategy.underline_to_camel);
|
||||
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
|
||||
strategy.setEntityLombokModel(true);
|
||||
strategy.setRestControllerStyle(true);
|
||||
strategy.setInclude("test_table"); // 测试表名
|
||||
strategy.setControllerMappingHyphenStyle(true);
|
||||
strategy.setTablePrefix("tb_");
|
||||
mpg.setStrategy(strategy);
|
||||
|
||||
// 模板引擎配置
|
||||
mpg.setTemplateEngine(new BeetlTemplateEnginePlus());
|
||||
|
||||
System.out.println("✅ 代码生成器配置成功!");
|
||||
System.out.println("✅ MyBatis-Plus Generator API 兼容性正常");
|
||||
System.out.println("✅ BeetlTemplateEnginePlus 加载成功");
|
||||
System.out.println("✅ 依赖版本降级成功,代码生成器已恢复正常");
|
||||
|
||||
// 注意:这里不执行实际的生成,只测试配置是否正确
|
||||
// mpg.execute();
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ 代码生成器测试失败: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '新增收货地址',
|
||||
navigationBarTextStyle: 'black'
|
||||
})
|
||||
@@ -1,343 +0,0 @@
|
||||
import {useEffect, useState, useRef} from "react";
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {Button, Loading, CellGroup, Input, TextArea, Form} from '@nutui/nutui-react-taro'
|
||||
import {Scan, ArrowRight} from '@nutui/icons-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {View} from '@tarojs/components'
|
||||
import {Address} from '@nutui/nutui-react-taro'
|
||||
import {ShopUserAddress} from "@/api/shop/shopUserAddress/model";
|
||||
import {getShopUserAddress, listShopUserAddress, updateShopUserAddress} from "@/api/shop/shopUserAddress";
|
||||
import RegionData from '@/api/json/regions-data.json';
|
||||
|
||||
const AddUserAddress = () => {
|
||||
const {params} = useRouter();
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
const [text, setText] = useState<string>('')
|
||||
const [optionsDemo1, setOptionsDemo1] = useState([])
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [FormData, setFormData] = useState<ShopUserAddress>({})
|
||||
const [inputText, setInputText] = useState<string>('')
|
||||
const formRef = useRef<any>(null)
|
||||
|
||||
const reload = async () => {
|
||||
const address = await getShopUserAddress(Number(params.id))
|
||||
setFormData(address)
|
||||
// 设置所在地区
|
||||
setText(`${address.province} ${address.city} ${address.region}`)
|
||||
// 整理地区数据
|
||||
setRegionData()
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理地区数据
|
||||
*/
|
||||
function setRegionData() {
|
||||
// @ts-ignore
|
||||
setOptionsDemo1(RegionData?.map((a) => {
|
||||
return {
|
||||
value: a.label,
|
||||
text: a.label,
|
||||
children: a.children?.map((b) => {
|
||||
return {
|
||||
value: b.label,
|
||||
text: b.label,
|
||||
children: b.children?.map((c) => {
|
||||
return {
|
||||
value: c.label,
|
||||
text: c.label
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* 地址识别功能
|
||||
*/
|
||||
const recognizeAddress = () => {
|
||||
if (!inputText.trim()) {
|
||||
Taro.showToast({
|
||||
title: '请输入要识别的文本',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = parseAddressText(inputText);
|
||||
|
||||
// 更新表单数据
|
||||
const newFormData = {
|
||||
...FormData,
|
||||
name: result.name || FormData.name,
|
||||
phone: result.phone || FormData.phone,
|
||||
address: result.address || FormData.address,
|
||||
province: result.province || FormData.province,
|
||||
city: result.city || FormData.city,
|
||||
region: result.region || FormData.region
|
||||
};
|
||||
|
||||
setFormData(newFormData);
|
||||
|
||||
// 更新地区显示文本
|
||||
if (result.province && result.city && result.region) {
|
||||
setText(`${result.province} ${result.city} ${result.region}`);
|
||||
}
|
||||
|
||||
// 更新表单字段值
|
||||
if (formRef.current) {
|
||||
formRef.current.setFieldsValue(newFormData);
|
||||
}
|
||||
|
||||
Taro.showToast({
|
||||
title: '识别成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 清空输入框
|
||||
setInputText('');
|
||||
|
||||
} catch (error) {
|
||||
Taro.showToast({
|
||||
title: '识别失败,请检查文本格式',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析地址文本
|
||||
*/
|
||||
const parseAddressText = (text: string) => {
|
||||
const result: any = {};
|
||||
|
||||
// 手机号正则 (11位数字)
|
||||
const phoneRegex = /1[3-9]\d{9}/;
|
||||
const phoneMatch = text.match(phoneRegex);
|
||||
if (phoneMatch) {
|
||||
result.phone = phoneMatch[0];
|
||||
}
|
||||
|
||||
// 姓名正则 (2-4个中文字符,通常在开头)
|
||||
const nameRegex = /^[\u4e00-\u9fa5]{2,4}/;
|
||||
const nameMatch = text.match(nameRegex);
|
||||
if (nameMatch) {
|
||||
result.name = nameMatch[0];
|
||||
}
|
||||
|
||||
// 省市区识别
|
||||
const regionResult = parseRegion(text);
|
||||
if (regionResult) {
|
||||
result.province = regionResult.province;
|
||||
result.city = regionResult.city;
|
||||
result.region = regionResult.region;
|
||||
}
|
||||
|
||||
// 详细地址提取 (去除姓名、手机号、省市区后的剩余部分)
|
||||
let addressText = text;
|
||||
if (result.name) {
|
||||
addressText = addressText.replace(result.name, '');
|
||||
}
|
||||
if (result.phone) {
|
||||
addressText = addressText.replace(result.phone, '');
|
||||
}
|
||||
if (result.province) {
|
||||
addressText = addressText.replace(result.province, '');
|
||||
}
|
||||
if (result.city) {
|
||||
addressText = addressText.replace(result.city, '');
|
||||
}
|
||||
if (result.region) {
|
||||
addressText = addressText.replace(result.region, '');
|
||||
}
|
||||
|
||||
// 清理地址文本
|
||||
result.address = addressText.replace(/[,,。\s]+/g, '').trim();
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析省市区
|
||||
*/
|
||||
const parseRegion = (text: string) => {
|
||||
// @ts-ignore
|
||||
for (const province of RegionData) {
|
||||
if (text.includes(province.label)) {
|
||||
const result: any = { province: province.label };
|
||||
|
||||
// 查找城市
|
||||
if (province.children) {
|
||||
for (const city of province.children) {
|
||||
if (text.includes(city.label)) {
|
||||
result.city = city.label;
|
||||
|
||||
// 查找区县
|
||||
if (city.children) {
|
||||
for (const region of city.children) {
|
||||
if (text.includes(region.label)) {
|
||||
result.region = region.label;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const submitSucceed = async (values: any) => {
|
||||
|
||||
const defaultAddress = await listShopUserAddress({isDefault: true})
|
||||
if(!defaultAddress) return
|
||||
|
||||
const setNotDefault = await updateShopUserAddress({
|
||||
...defaultAddress[0],
|
||||
isDefault: false
|
||||
})
|
||||
if(!setNotDefault) return
|
||||
|
||||
updateShopUserAddress({
|
||||
...values,
|
||||
id: Number(params.id),
|
||||
province: FormData.province,
|
||||
city: FormData.city,
|
||||
region: FormData.region
|
||||
}).then(() => {
|
||||
Taro.showToast({title: `保存成功`, icon: 'success'})
|
||||
setTimeout(() => {
|
||||
return Taro.navigateBack()
|
||||
}, 1000)
|
||||
}).catch(() => {
|
||||
Taro.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'error'
|
||||
});
|
||||
})
|
||||
}
|
||||
const submitFailed = (error: any) => {
|
||||
console.log(error, 'err...')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload().then(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return <Loading className={'px-2'}>加载中</Loading>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form
|
||||
ref={formRef}
|
||||
divider
|
||||
initialValues={FormData}
|
||||
labelPosition="left"
|
||||
onFinish={(values) => submitSucceed(values)}
|
||||
onFinishFailed={(errors) => submitFailed(errors)}
|
||||
footer={
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
nativeType="submit"
|
||||
type="success"
|
||||
size="large"
|
||||
className={'w-full'}
|
||||
block
|
||||
>
|
||||
保存并使用
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<CellGroup className={'px-3'}>
|
||||
<div
|
||||
style={{
|
||||
border: '1px dashed #22c55e',
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
justifyContent: 'space-between',
|
||||
padding: '4px',
|
||||
position: 'relative'
|
||||
}}>
|
||||
|
||||
<TextArea
|
||||
style={{height: '100px'}}
|
||||
value={inputText}
|
||||
onChange={(value) => setInputText(value)}
|
||||
placeholder={'请粘贴或输入文本,点击"识别"自动识别收货人姓名、地址、电话'}
|
||||
/>
|
||||
<Button
|
||||
icon={<Scan/>}
|
||||
style={{position: 'absolute', right: '10px', bottom: '10px'}}
|
||||
type="success"
|
||||
size={'small'}
|
||||
fill="dashed"
|
||||
onClick={recognizeAddress}
|
||||
>
|
||||
识别
|
||||
</Button>
|
||||
</div>
|
||||
</CellGroup>
|
||||
<View className={'bg-gray-100 h-3'}></View>
|
||||
<CellGroup style={{padding: '4px 0'}}>
|
||||
<Form.Item name="name" label="收货人" initialValue={FormData.name} required>
|
||||
<Input placeholder="请输入收货人姓名" maxLength={10}/>
|
||||
</Form.Item>
|
||||
<Form.Item name="phone" label="手机号" initialValue={FormData.phone} required>
|
||||
<Input placeholder="请输入手机号" maxLength={11}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="所在地区"
|
||||
name="region"
|
||||
initialValue={FormData.region}
|
||||
rules={[{message: '请输入您的所在地区'}]}
|
||||
required
|
||||
>
|
||||
<div className={'flex justify-between items-center'} onClick={() => setVisible(true)}>
|
||||
<Input placeholder="选择所在地区" value={text} disabled/>
|
||||
<ArrowRight className={'text-gray-400'}/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item name="address" label="收货地址" initialValue={FormData.address} required>
|
||||
<TextArea maxLength={50} placeholder="请输入详细收货地址"/>
|
||||
</Form.Item>
|
||||
</CellGroup>
|
||||
</Form>
|
||||
|
||||
<Address
|
||||
visible={visible}
|
||||
options={optionsDemo1}
|
||||
title="选择地址"
|
||||
onChange={(value, _) => {
|
||||
setFormData({
|
||||
...FormData,
|
||||
province: `${value[0]}`,
|
||||
city: `${value[1]}`,
|
||||
region: `${value[2]}`
|
||||
})
|
||||
setText(value.join(' '))
|
||||
}}
|
||||
onClose={() => setVisible(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddUserAddress;
|
||||
@@ -1,4 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '地址管理',
|
||||
navigationBarTextStyle: 'black'
|
||||
})
|
||||
@@ -1,151 +0,0 @@
|
||||
import {useState} from "react";
|
||||
import Taro, {useDidShow} from '@tarojs/taro'
|
||||
import {Button, Cell, CellGroup, Space, Empty, ConfigProvider, Divider} from '@nutui/nutui-react-taro'
|
||||
import {Dongdong, ArrowRight, CheckNormal, Checked} from '@nutui/icons-react-taro'
|
||||
import {View} from '@tarojs/components'
|
||||
import {ShopUserAddress} from "@/api/shop/shopUserAddress/model";
|
||||
import {listShopUserAddress, removeShopUserAddress, updateShopUserAddress} from "@/api/shop/shopUserAddress";
|
||||
|
||||
const Address = () => {
|
||||
const [list, setList] = useState<ShopUserAddress[]>([])
|
||||
const [address, setAddress] = useState<ShopUserAddress>()
|
||||
|
||||
const reload = () => {
|
||||
listShopUserAddress({
|
||||
userId: Taro.getStorageSync('UserId')
|
||||
})
|
||||
.then(data => {
|
||||
setList(data || [])
|
||||
// 默认地址
|
||||
setAddress(data.find(item => item.isDefault))
|
||||
})
|
||||
.catch(() => {
|
||||
Taro.showToast({
|
||||
title: '获取地址失败',
|
||||
icon: 'error'
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const onDefault = async (item: ShopUserAddress) => {
|
||||
if (address) {
|
||||
await updateShopUserAddress({
|
||||
...address,
|
||||
isDefault: false
|
||||
})
|
||||
}
|
||||
await updateShopUserAddress({
|
||||
id: item.id,
|
||||
isDefault: true
|
||||
})
|
||||
Taro.showToast({
|
||||
title: '设置成功',
|
||||
icon: 'success'
|
||||
});
|
||||
reload();
|
||||
}
|
||||
|
||||
const onDel = async (id?: number) => {
|
||||
await removeShopUserAddress(id)
|
||||
Taro.showToast({
|
||||
title: '删除成功',
|
||||
icon: 'success'
|
||||
});
|
||||
reload();
|
||||
}
|
||||
|
||||
const selectAddress = async (item: ShopUserAddress) => {
|
||||
if (address) {
|
||||
await updateShopUserAddress({
|
||||
...address,
|
||||
isDefault: false
|
||||
})
|
||||
}
|
||||
await updateShopUserAddress({
|
||||
id: item.id,
|
||||
isDefault: true
|
||||
})
|
||||
setTimeout(() => {
|
||||
Taro.navigateBack()
|
||||
},500)
|
||||
}
|
||||
|
||||
useDidShow(() => {
|
||||
reload()
|
||||
});
|
||||
|
||||
if (list.length == 0) {
|
||||
return (
|
||||
<ConfigProvider>
|
||||
<div className={'h-full flex flex-col justify-center items-center'} style={{
|
||||
height: 'calc(100vh - 300px)',
|
||||
}}>
|
||||
<Empty
|
||||
style={{
|
||||
backgroundColor: 'transparent'
|
||||
}}
|
||||
description="您还没有地址哦"
|
||||
/>
|
||||
<Space>
|
||||
<Button onClick={() => Taro.navigateTo({url: '/user/address/add'})}>新增地址</Button>
|
||||
<Button type="success" fill="dashed"
|
||||
onClick={() => Taro.navigateTo({url: '/user/address/wxAddress'})}>获取微信地址</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</ConfigProvider>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<CellGroup>
|
||||
<Cell
|
||||
onClick={() => Taro.navigateTo({url: '/user/address/wxAddress'})}
|
||||
>
|
||||
<div className={'flex justify-between items-center w-full'}>
|
||||
<div className={'flex items-center gap-3'}>
|
||||
<Dongdong className={'text-green-600'}/>
|
||||
<div>获取微信地址</div>
|
||||
</div>
|
||||
<ArrowRight className={'text-gray-400'}/>
|
||||
</div>
|
||||
</Cell>
|
||||
</CellGroup>
|
||||
{list.map((item, _) => (
|
||||
<Cell.Group>
|
||||
<Cell className={'flex flex-col gap-1'} onClick={() => selectAddress(item)}>
|
||||
<View>
|
||||
<View className={'font-medium text-sm'}>{item.name} {item.phone}</View>
|
||||
</View>
|
||||
<View className={'text-xs'}>
|
||||
{item.province} {item.city} {item.region} {item.address}
|
||||
</View>
|
||||
</Cell>
|
||||
<Cell
|
||||
align="center"
|
||||
title={
|
||||
<View className={'flex items-center gap-1'} onClick={() => onDefault(item)}>
|
||||
{item.isDefault ? <Checked className={'text-green-600'} size={16}/> : <CheckNormal size={16}/>}
|
||||
<View className={'text-gray-400'}>默认地址</View>
|
||||
</View>
|
||||
}
|
||||
extra={
|
||||
<>
|
||||
<View className={'text-gray-400'} onClick={() => onDel(item.id)}>
|
||||
删除
|
||||
</View>
|
||||
<Divider direction={'vertical'}/>
|
||||
<View className={'text-gray-400'}
|
||||
onClick={() => Taro.navigateTo({url: '/user/address/add?id=' + item.id})}>
|
||||
修改
|
||||
</View>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Cell.Group>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Address;
|
||||
@@ -1,164 +0,0 @@
|
||||
# hideInTable 表格列优化方案总结
|
||||
|
||||
## 🎯 方案概述
|
||||
|
||||
使用 `hideInTable: true` 属性来控制表格列的默认显示,这是一个既保留完整功能又保持界面简洁的最佳方案。
|
||||
|
||||
## ✨ 核心优势
|
||||
|
||||
### 1. 🔄 **完整性 + 简洁性**
|
||||
- **生成所有字段**:不丢失任何数据库字段信息
|
||||
- **默认简洁**:只显示最重要的核心字段
|
||||
- **用户可控**:通过列设置功能显示需要的字段
|
||||
|
||||
### 2. 🎛️ **灵活的用户体验**
|
||||
- **开箱即用**:默认显示最常用的字段
|
||||
- **按需扩展**:用户可以勾选显示更多字段
|
||||
- **个性化**:不同用户可以有不同的列显示偏好
|
||||
|
||||
### 3. 🚀 **技术优势**
|
||||
- **标准化**:使用 Ant Design 标准的 `hideInTable` 属性
|
||||
- **兼容性好**:与现有表格组件完美兼容
|
||||
- **维护简单**:不需要复杂的逻辑,只需要简单的布尔值控制
|
||||
|
||||
## 📋 实现细节
|
||||
|
||||
### 核心字段判断规则
|
||||
```javascript
|
||||
// 以下字段默认显示(最多6个)
|
||||
- 主键字段 (id)
|
||||
- 名称字段 (name)
|
||||
- 标题字段 (title)
|
||||
- 编码字段 (code)
|
||||
- 状态字段 (status)
|
||||
- 排序字段 (sort)
|
||||
- 创建时间 (createTime)
|
||||
```
|
||||
|
||||
### 隐藏字段规则
|
||||
```javascript
|
||||
// 以下字段默认隐藏 (hideInTable: true)
|
||||
- 更新时间 (updateTime)
|
||||
- 创建人 (createBy)
|
||||
- 更新人 (updateBy)
|
||||
- 备注 (remark)
|
||||
- 描述 (description)
|
||||
- 内容 (content)
|
||||
- 其他非核心业务字段
|
||||
```
|
||||
|
||||
## 🔧 生成的代码示例
|
||||
|
||||
```javascript
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 90,
|
||||
align: 'center',
|
||||
// 核心字段,默认显示
|
||||
},
|
||||
{
|
||||
title: '文章标题',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
ellipsis: true,
|
||||
// 核心字段,默认显示
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
// 核心字段,默认显示
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
ellipsis: true,
|
||||
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd'),
|
||||
// 核心字段,默认显示
|
||||
},
|
||||
{
|
||||
title: '创建人',
|
||||
dataIndex: 'createBy',
|
||||
key: 'createBy',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
hideInTable: true, // 非核心字段,默认隐藏
|
||||
},
|
||||
{
|
||||
title: '文章内容',
|
||||
dataIndex: 'content',
|
||||
key: 'content',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
ellipsis: true,
|
||||
hideInTable: true, // 长文本字段,默认隐藏
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
hideInSetting: true, // 操作列不允许隐藏
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
## 🎨 用户界面效果
|
||||
|
||||
### 默认显示
|
||||
```
|
||||
┌────┬──────────┬────┬──────────┬────────┐
|
||||
│ ID │ 文章标题 │状态│ 创建时间 │ 操作 │
|
||||
├────┼──────────┼────┼──────────┼────────┤
|
||||
│ 1 │ 技术文章 │启用│2024-01-01│修改|删除│
|
||||
│ 2 │ 产品介绍 │禁用│2024-01-02│修改|删除│
|
||||
└────┴──────────┴────┴──────────┴────────┘
|
||||
```
|
||||
|
||||
### 用户勾选更多列后
|
||||
```
|
||||
┌────┬──────────┬────┬────────┬──────────┬──────────┬────────┐
|
||||
│ ID │ 文章标题 │状态│ 创建人 │ 创建时间 │ 文章内容 │ 操作 │
|
||||
├────┼──────────┼────┼────────┼──────────┼──────────┼────────┤
|
||||
│ 1 │ 技术文章 │启用│ 张三 │2024-01-01│ 这是... │修改|删除│
|
||||
│ 2 │ 产品介绍 │禁用│ 李四 │2024-01-02│ 产品... │修改|删除│
|
||||
└────┴──────────┴────┴────────┴──────────┴──────────┴────────┘
|
||||
```
|
||||
|
||||
## 🔄 与其他方案对比
|
||||
|
||||
| 方案 | 完整性 | 简洁性 | 用户控制 | 维护成本 | 推荐度 |
|
||||
|------|--------|--------|----------|----------|--------|
|
||||
| 显示所有字段 | ✅ | ❌ | ❌ | 低 | ⭐⭐ |
|
||||
| 过滤显示部分字段 | ❌ | ✅ | ❌ | 中 | ⭐⭐⭐ |
|
||||
| **hideInTable方案** | ✅ | ✅ | ✅ | 低 | ⭐⭐⭐⭐⭐ |
|
||||
|
||||
## 📝 使用建议
|
||||
|
||||
1. **保持核心字段数量在6个以内**:确保表格在标准屏幕上显示良好
|
||||
2. **优先显示业务关键字段**:如名称、状态、时间等
|
||||
3. **长文本字段默认隐藏**:如备注、描述、内容等
|
||||
4. **提供列设置入口**:让用户可以方便地自定义显示列
|
||||
5. **考虑移动端适配**:在小屏幕上进一步减少默认显示列
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
`hideInTable` 方案是表格列显示的最佳实践,它完美平衡了:
|
||||
- **开发效率**:自动生成,无需手动配置
|
||||
- **用户体验**:默认简洁,按需扩展
|
||||
- **功能完整**:不丢失任何字段信息
|
||||
- **维护成本**:标准化实现,易于维护
|
||||
|
||||
这个方案让生成的表格既专业又实用,是代码生成器的理想选择!
|
||||
Reference in New Issue
Block a user