生成配置文件及结构
引入freemarker依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
<scope>test</scope>
</dependency>
vue前端页面生成模板
根据需求可自行调整
<template>
<!--createTime:${date} ,author:${author} -->
<div class="app-container">
<!--搜索框-->
<el-form :inline="false" size="medium" v-if="showSearchBox" :style="{ 'height': searchBoxHeight + 'px' }"
label-position="right" label-width="130px">
<el-row >
<#list searchModels as item>
<#if item_index!=0>
<el-col :span="6">
<el-form-item label="${item.label}">
<#if item.datePicker==true >
<div class="block">
<el-date-picker
v-model="serverQuery.${item.value}"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
value-format="yyyy-MM-dd"
class="dialog_input"
@change="search"
end-placeholder="结束日期">
</el-date-picker>
</div>
</#if>
<#if item.checkState==true >
<div class="block">
<el-select v-model="serverQuery.${item.value}" placeholder="请选择" style="width:100%;" clearable @change="search">
<el-option
v-for="item in stateList"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
</#if>
<#if item.dropDown==true >
<div class="block">
<el-select v-model="serverQuery.${item.dropdownInfo.value}" placeholder="请选择" clearable class="dialog_input">
<el-option
v-for="item in ${item.dropdownInfo.value}Options"
:label="item.${item.dropdownInfo.label}"
:value="item.${item.dropdownInfo.value}">
</el-option>
</el-select>
</div>
</#if>
<#if item.checkState==false&&item.datePicker==false&&item.dropDown==false >
<el-input placeholder="请输入" v-model="serverQuery.${item.value}" clearable style="width:100%;"
@keyup.enter.native="search"></el-input>
</#if>
</el-form-item>
</el-col>
</#if>
</#list>
<el-col :span="6" style="text-align: right;">
<el-form-item>
<el-button @click="resetSearch">清空</el-button>
<el-button type="success" @click="search">搜索</el-button>
</el-form-item>
</el-col>
</el-row>
<!--样式分割线-->
<el-row>
<el-col class="search-bar-height">
<el-divider></el-divider>
</el-col>
</el-row>
</el-form>
<!--按钮框-->
<el-form :inline="true">
<el-row>
<el-col :span="12">
<!--左侧按钮-->
<el-form-item class="fixed_left">
<PermissionButton size="medium" label="添加${modelName}(两列)" icon="el-icon-plus" perms="1000" type="success" @click="openDialog(true)" />
<PermissionButton size="medium" label="添加${modelName}(三列)" icon="el-icon-plus" perms="1000" type="success" @click="openDialogThree(true)" />
</el-form-item>
</el-col>
<el-col :span="12">
<!--右侧按钮-->
<el-form-item class="fixed_right">
<!--展示/关闭搜索框按钮-->
<el-button size="medium" icon="el-icon-search" circle @click="clickSearch"></el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!--数据展示-->
<div>
<el-table v-loading="listLoading" :height="tableHeight - (showSearchBox ? searchBoxHeight : 0)" :data="listData" border
el-table-column fit highlight-current-row>
<#list searchModels as item>
<#if item_index = 0 >
<el-table-column align="center" prop="${item.value}" label="${item.label}" :show-overflow-tooltip='true' >
<template slot-scope="scope">
<el-link type="primary" @click="getById(scope.row.${item.value}, true)">{{ scope.row.${item.value} }}</el-link>
</template>
</el-table-column>
</#if>
<#if item_index != 0 >
<el-table-column align="center" prop="${item.value}" label="${item.label}" :show-overflow-tooltip='true' min-width="200">
<#if item.checkState==true>
<template slot-scope="scope">
<el-tag v-if="scope.row.${item.value}" type="success">是</el-tag>
<el-tag v-else type="info">否</el-tag>
</template>
</#if>
</el-table-column>
</#if>
</#list>
<el-table-column align="center" label="操作" fixed="right" width="200px">
<template slot-scope="scope">
<#list searchModels as item>
<#if item_index = 0 >
<PermissionButton size="mini" perms="1000" type="primary" label="编辑"
@click.native.stop="getById(scope.row.${item.value}, true)" />
<PermissionButton size="mini" perms="1000" type="danger" label="删除"
@click.native.stop="delById(scope.row.${item.value})" />
</#if>
</#list>
</template>
</el-table-column>
</el-table>
<el-pagination
style="margin-top:15px"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="serverQuery.page"
:page-sizes="[20,40, 60, 80, 100]"
:page-size.sync="serverQuery.size"
layout="total, sizes, prev, pager, next, jumper"
:total="serverQuery.total"
></el-pagination>
</div>
<!--添加/${modelName}信息两列-->
<el-dialog v-dialogDrag top="1%" :modal-append-to-body="false" :title="dialog_title" width="1100px" :visible.sync="dialogBaseFlag"
draggable :lock-scroll="false" :modal="true"
@before-close="this.$refs['ruleForm'].clearValidate()" class="custom_append">
<div class="div_center" style="max-height:700px;">
<!--添加${modelName}信息-->
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" size="medium" label-position="right"
label-width="135px">
<div class="divider"><span>${modelName}信息</span><p></p></div>
<el-row>
<#list searchModels as item>
<#if item_index!=0>
<el-col :span="12">
<el-form-item label="${item.label}" prop="${item.value}">
<#if item.datePicker==true >
<div class="block">
<el-date-picker
v-model="ruleForm.${item.value}"
type="date"
value-format="yyyy-MM-dd"
class="dialog_input"
placeholder="选择日期时间">
</el-date-picker>
</div>
</#if>
<#if item.checkState==true >
<div class="block">
<el-switch v-model="ruleForm.${item.value}" active-color="#13ce66" inactive-color="#ff4949"></el-switch>
</div>
</#if>
<#if item.dropDown==true >
<div class="block">
<el-select v-model="ruleForm.${item.dropdownInfo.value}" placeholder="请选择" class="dialog_input">
<el-option
v-for="item in ${item.dropdownInfo.value}Options"
:label="item.${item.dropdownInfo.label}"
:value="item.${item.dropdownInfo.value}">
</el-option>
</el-select>
</div>
</#if>
<#if item.checkState==false&&item.datePicker==false&&item.dropDown==false >
<el-input placeholder="请输入" v-model="ruleForm.${item.value}" class="dialog_input"></el-input>
</#if>
</el-form-item>
</el-col>
</#if>
</#list>
</el-row>
</el-form>
</div>
<div slot="footer" class="dialog-footer" style="text-align: center">
<el-button size="medium" @click="dialogBaseFlag = false">取 消</el-button>
<el-button size="medium" type="success" :disabled="submitButtonFlag" @click="submitForm('ruleForm')">提 交
</el-button>
</div>
</el-dialog>
<!--添加/${modelName}信息三列-->
<el-dialog v-dialogDrag :title="dialog_title" width="1350px" top="1%"
:visible.sync="dialogBaseFlagThree" :modal="true" @before-close="this.$refs['ruleForm'].clearValidate()"
class="custom_append">
<div class="div_center" style="max-height:700px;">
<!--添加${modelName}信息-->
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" size="medium" label-position="right"
label-width="135px">
<div class="divider"><span>${modelName}信息</span><p></p></div>
<el-row>
<#list searchModels as item>
<#if item_index!=0>
<el-col :span="12">
<el-form-item label="${item.label}" prop="${item.value}">
<#if item.datePicker==true >
<div class="block">
<el-date-picker
v-model="ruleForm.${item.value}"
type="date"
value-format="yyyy-MM-dd"
class="dialog_input"
placeholder="选择日期时间">
</el-date-picker>
</div>
</#if>
<#if item.checkState==true >
<div class="block">
<el-switch v-model="ruleForm.${item.value}" active-color="#13ce66" inactive-color="#ff4949"></el-switch>
</div>
</#if>
<#if item.dropDown==true >
<div class="block">
<el-select v-model="ruleForm.${item.dropdownInfo.value}" placeholder="请选择" class="dialog_input">
<el-option
v-for="item in ${item.dropdownInfo.value}Options"
:label="item.${item.dropdownInfo.label}"
:value="item.${item.dropdownInfo.value}">
</el-option>
</el-select>
</div>
</#if>
<#if item.checkState==false&&item.datePicker==false&&item.dropDown==false >
<el-input placeholder="请输入" v-model="ruleForm.${item.value}" class="dialog_input"></el-input>
</#if>
</el-form-item>
</el-col>
</#if>
</#list>
</el-row>
</el-form>
</div>
<div slot="footer" class="dialog-footer" style="text-align: center">
<el-button size="medium" @click="dialogBaseFlagThree = false">取 消</el-button>
<el-button size="medium" type="success" :disabled="submitButtonFlag" @click="submitForm('ruleForm')">提 交
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { sendPostByJson, sendPostByKeyValue,sendGet } from '@/utils/httpUtils'
import FormValidate from '@/utils/validate'
import Tinymce from '@/components/Tinymce'
export default {
components: { Tinymce },
data() {
return {
baseImgURL: undefined,
headers: undefined,
//展示表格高度
tableHeight: window.innerHeight - 230,
//搜索框展示高度
searchBoxHeight: 230,
<#list searchModels as item>
<#if item.dropDown==true>
//${item.label}集合
${item.dropdownInfo.value}Options:[],
</#if>
</#list>
//是否显示搜索框
showSearchBox: false,
//查询数据容器
serverQuery: {
<#list searchModels as item>
//${item.label}
${item.value} :undefined,
</#list>
total: 0,
page: 1,
size: 50
},
dialog_title: '添加${modelName}',
dialogBaseFlag: false,
dialogBaseFlagThree: false,
listLoading: false,
//按钮置灰
submitButtonFlag: false,
//${modelName}集合容器
listData: [],
//${modelName}容器
ruleForm: {
<#list searchModels as item>
<#if item.checkState==false>
//${item.label}
${item.value} :false,
</#if>
<#if item.checkState!=false>
//${item.label}
${item.value} :undefined,
</#if>
</#list>
},
//是否状态查询
stateList: [
{ value: undefined, label: '全部' },
{ value: true, label: '是' },
{ value: false, label: '否' },
],
//修改规则
rules: {
<#list searchModels as item>
${item.value}: [
{ required: true, message: "${item.label}不能为空", trigger: "change" },
],
</#list>
},
//${modelName}类容器
ruleFormProductClass: {},
//${modelName}类集合
productClassList: [],
//${modelName}类集合还原
productClassListRestoreData: [],
//${modelName}类是否加载
listLoadingProductClass: false,
//按钮置灰
submitButtonFlagProductClass: false,
};
},
mounted: function () {
this.baseImgURL = process.env.VUE_APP_BASE_IMG
this.headers = {
Token: this.$store.state.user.token,
};
//初始化数据
this.search()
this.initFun()
},
methods: {
//初始化选择查询的集合
async initFun(){
<#list searchModels as item>
<#if item.dropDown==true>
const res${item_index} = await sendPostByKeyValue("/${item.dropdownInfo.tableName}/listNoPage")
if (res${item_index}&&res${item_index}.code == 200) {
this.${item.dropdownInfo.value}Options = res${item_index}.data;
}
</#if>
</#list>
},
handleSizeChange(val) {
//查询
this.serverQuery.size = val;
this.search();
},
handleCurrentChange(val) {
//查询
this.serverQuery.page = val;
this.search();
},
/**
* 触发查询图标
*/
clickSearch() {
this.showSearchBox = !this.showSearchBox
},
/**
* 重置${modelName}容器
*/
resetRuleForm() {
this.ruleForm = {
<#list searchModels as item>
//${item.label}
${item.value} :undefined,
</#list>
}
},
/**
* 重置${modelName}类容器
*/
resetRuleFormProductClass() {
this.ruleFormProductClass = {
<#list searchModels as item>
//${item.label}
${item.value} :undefined,
</#list>
}
},
/**
* 查询${modelName}信息
* @param productId
* @param detail
* @returns {Promise<void>}
*/
async getById(productId, detail = false) {
this.openDialogThree(false)
try {
const res = await sendPostByKeyValue('/${apiName}/detail', { id: productId })
if (res && res.code === 200) {
this.ruleForm = res.data
} else {
this.$message({ type: 'error', message: res && res.message ? res.message : '获取详情失败' })
}
} catch (e) {
console.log(e.toString())
}
},
/**
*保存${modelName}
* @returns {Promise<void>}
*/
async save() {
this.submitButtonFlag = true
try {
let res
var message
if(this.ruleForm.${id}){
res = await sendPostByJson('/${apiName}/update', this.ruleForm)
message='更新'
}else{
res = await sendPostByJson('/${apiName}/add', this.ruleForm)
message='保存'
}
this.submitButtonFlag = false
if (res && res.code === 200) {
await this.search()
this.$message({ type: 'success', message: message+'成功' })
} else {
this.$message({ type: 'error', message: res && res.message ? res.message : message+'失败' })
}
this.dialogBaseFlag = false
} catch (e) {
console.log(e.toString())
} finally {
this.submitButtonFlag = false
this.dialogBaseFlagThree = false
}
},
/**
* 打开添加/修改${modelName}弹窗s
* @param flag
*/
openDialog(flag) {
this.dialogBaseFlag = true
this.dialog_title = flag ? '添加${modelName}' : '${modelName}信息'
this.resetRuleForm()
this.$nextTick(() => {
this.$refs['ruleForm'].clearValidate()
})
},
openDialogThree(flag) {
this.dialogBaseFlagThree = true
this.dialog_title = flag ? '添加${modelName}' : '${modelName}信息'
this.resetRuleForm()
this.$nextTick(() => {
this.$refs['ruleForm'].clearValidate()
})
},
//删除
delById(id) {
this.$confirm("确定要删除该记录?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
lockScroll: false,
type: "warning",
center: true,
beforeClose: (action, instance, done) => {
// 确定
if (action === "confirm") {
// 删除操作
sendGet("/${apiName}/delete?id=" + id)
.then((response) => {
if (response.code == 200) {
this.search();
this.$message({
type: "success",
message: "删除成功",
});
} else {
this.$message({
type: "error",
message: response.message,
});
}
})
.catch((err) => {
console.log(err);
});
done();
} else {
done();
}
},
}).catch(() => {
this.$message({
type: "info",
message: "已取消",
});
});
},
/**
* 保存按钮
* @param formName
*/
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.save()
} else {
console.log('error submit!!')
return false
}
})
},
/**
* 重置查询条件
* @returns {Promise<void>}
*/
async resetSearch() {
this.serverQuery = {
<#list searchModels as item>
//${item.label}
${item.value} :undefined,
</#list>
total: 0,
page: 1,
size: 50
}
await this.search();
},
/**
* 查询
* @returns {Promise<void>}
*/
async search() {
this.listLoading = true
try {
const url = '/${apiName}/list'
const res = await sendPostByJson(url, this.serverQuery)
if (res && res.code === 200) {
this.listData = res.data.list
this.serverQuery.total = res.data.total
}
} catch (e) {
console.log(e.toString())
} finally {
this.listLoading = false
}
},
/**
* 点击弹出修改框
* @param row
* @param column
* @param event
*/
rowClick: function (row, column, event) {
this.getById(row.productId, true)
}
}
}
</script>
<style scoped>
</style>
java生成执行文件
public static void main(String[] args) {
// String generatorInfoExample="{
// "vueFileUrl": "C:/Users/adminDesk/Documents/webadmin/src/views/test-role",
// "dropDownInfo": [
// {
// "value": "product_id",
// "label": "product_name",
// "tableName": "product"
// },
// {
// "value": "product_id",
// "label": "product_name",
// "tableName": "product"
// }
// ]
// }";
String generatorTableOtherInfo="{\n" +
" \"vueFileUrl\": \"C:/Users/adminDesk/Documents/jfl-java-seed/webadmin/src/views/test-role\",\n" +
" \"dropDownInfo\": [\n" +
" {\n" +
" \"value\": \"product_id\",\n" +
" \"label\": \"product_name\",\n" +
" \"tableName\": \"product\"\n" +
" }\n" +
" ]\n" +
"}";
//实体类 表名 其它参数
genVue2(AnotherTest.class,"another_test",generatorTableOtherInfo);
}
private static void genVue2(Class<?> modelClass,String tableName,String generatorTableOtherInfo) {
genView(tableName,modelClass,generatorTableOtherInfo);
}
private static void genView(String tableName,Class<?> modelClass,String generatorTableOtherInfo) {
try {
freemarker.template.Configuration cfg = getConfiguration();
Map<String, Object> data = new HashMap<>();
List<SearchModel> searchModels = new ArrayList<>();
String apiName =CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, tableName);
data.put("date", DATE);
data.put("apiName",apiName);
data.put("author", AUTHOR);
data.put("tableName", tableName);//给vue文件名赋值
//生成到对应的路径去
GeneratorOtherInfo info =JSONObject.parseObject(generatorTableOtherInfo, GeneratorOtherInfo.class);
File file = new File(info.getVueFileUrl()+"/" + tableName + ".vue");
int lastSlashIndex = info.getVueFileUrl().lastIndexOf('/');
//获取上级目录example:usr-role
String parentRole=null;
if (lastSlashIndex != -1) {
parentRole = info.getVueFileUrl().substring(lastSlashIndex + 1);
}
//搜索框赋值
generatorSearchModels(modelClass,searchModels,info);
data.put("searchModels",searchModels);
data.put("id",searchModels.get(0).getValue());
data.put("modelName",searchModels.get(0).getLabel().replaceAll("id",""));
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
cfg.getTemplate("vue.ftl").process(data, new FileWriter(file));
}catch (Exception e) {
throw new RuntimeException("生成Vue2页面失败失败", e);
}
}
前端页面需要的参数值,根据需要调整
private static void generatorSearchModels(Class<?> modelClass,List<SearchModel> searchModels,GeneratorOtherInfo info) {
Field[] allFields = FieldUtils.getAllFields(modelClass);
List<Field> fields = Arrays.asList(allFields);
for (Field field: fields) {
field.setAccessible(true);
//通过注解反射获取字段信息
ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
SearchModel searchModel = new SearchModel();
if(field.getType()==Boolean.class){
searchModel.setCheckState(true);
}
if(field.getType()== Date.class){
searchModel.setDatePicker(true);
}
if(CollUtil.isNotEmpty(info.getDropDownInfo())){
boolean isDropDown = info.getDropDownInfo().stream().anyMatch(item -> CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL,item.getValue()).equals(field.getName()));
if(isDropDown){
searchModel.setDropDown(true);
}
info.getDropDownInfo().stream().forEach(item->{
if((CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, item.getValue())).equals(field.getName())){
Dropdown dropdown = new Dropdown();
dropdown.setTableName(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, item.getTableName()));
dropdown.setValue(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, item.getValue()));
dropdown.setLabel(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, item.getLabel()));
searchModel.setDropdownInfo(dropdown);
}
});
}
//处理下拉框选项属性
searchModel.setLabel(annotation!=null? annotation.value():"搜索标题");
searchModel.setValue(field.getName());
searchModels.add(searchModel);
}
}
参数类
public class SearchModel {
private String label;//标题
private String value;//值
private Dropdown dropdownInfo;
private Boolean datePicker=false;//是否时间字段
private Boolean checkState=false;//是否判断字段
private Boolean dropDown=false;//是否下拉字段
public Dropdown getDropdownInfo() {
return dropdownInfo;
}
public void setDropdownInfo(Dropdown dropdownInfo) {
this.dropdownInfo = dropdownInfo;
}
public Boolean getDropDown() {
return dropDown;
}
public void setDropDown(Boolean dropDown) {
this.dropDown = dropDown;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Boolean getDatePicker() {
return datePicker;
}
public void setDatePicker(Boolean datePicker) {
this.datePicker = datePicker;
}
public Boolean getCheckState() {
return checkState;
}
public void setCheckState(Boolean checkState) {
this.checkState = checkState;
}
}
执行main方法即可生成页面,生成vue文件位置可通过vuefileurl调整.
这个博客主要是提供一个思路,可以更加这个文章进行调整,同时也是一个解放开发生产力的工具