Commit da0f9ca5284efe3b6dd4e33f370bbc65d70371d1
1 parent
ce80c6f9
feat: 代码生成 vben
Showing
6 changed files
with
321 additions
and
0 deletions
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java
| @@ -15,6 +15,7 @@ public enum CodegenFrontTypeEnum { | @@ -15,6 +15,7 @@ public enum CodegenFrontTypeEnum { | ||
| 15 | VUE2(10), // Vue2 Element UI 标准模版 | 15 | VUE2(10), // Vue2 Element UI 标准模版 |
| 16 | VUE3(20), // Vue3 Element Plus 标准模版 | 16 | VUE3(20), // Vue3 Element Plus 标准模版 |
| 17 | VUE3_SCHEMA(21), // Vue3 Element Plus Schema 模版 | 17 | VUE3_SCHEMA(21), // Vue3 Element Plus Schema 模版 |
| 18 | + VUE3_VBEN(30), // Vue3 VBEN 模版 | ||
| 18 | ; | 19 | ; |
| 19 | 20 | ||
| 20 | /** | 21 | /** |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
| @@ -116,6 +116,17 @@ public class CodegenEngine { | @@ -116,6 +116,17 @@ public class CodegenEngine { | ||
| 116 | vue3FilePath("views/${table.moduleName}/${classNameVar}/index.vue")) | 116 | vue3FilePath("views/${table.moduleName}/${classNameVar}/index.vue")) |
| 117 | .put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/form.vue"), | 117 | .put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/form.vue"), |
| 118 | vue3FilePath("views/${table.moduleName}/${classNameVar}/${simpleClassName}Form.vue")) | 118 | vue3FilePath("views/${table.moduleName}/${classNameVar}/${simpleClassName}Form.vue")) |
| 119 | + .put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("api/api.ts"), | ||
| 120 | + vue3FilePath("api/${table.moduleName}/${classNameVar}/index.ts")) | ||
| 121 | + // Vue3 vben 模版 | ||
| 122 | + .put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/data.ts"), | ||
| 123 | + vue3FilePath("views/${table.moduleName}/${classNameVar}/${classNameVar}.data.ts")) | ||
| 124 | + .put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/index.vue"), | ||
| 125 | + vue3FilePath("views/${table.moduleName}/${classNameVar}/index.vue")) | ||
| 126 | + .put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("views/form.vue"), | ||
| 127 | + vue3FilePath("views/${table.moduleName}/${classNameVar}/${simpleClassName}Modal.vue")) | ||
| 128 | + .put(CodegenFrontTypeEnum.VUE3_VBEN.getType(), vue3VbenTemplatePath("api/api.ts"), | ||
| 129 | + vue3FilePath("api/${table.moduleName}/${classNameVar}/index.ts")) | ||
| 119 | .build(); | 130 | .build(); |
| 120 | 131 | ||
| 121 | @Resource | 132 | @Resource |
| @@ -283,4 +294,8 @@ public class CodegenEngine { | @@ -283,4 +294,8 @@ public class CodegenEngine { | ||
| 283 | private static String vue3SchemaTemplatePath(String path) { | 294 | private static String vue3SchemaTemplatePath(String path) { |
| 284 | return "codegen/vue3_schema/" + path + ".vm"; | 295 | return "codegen/vue3_schema/" + path + ".vm"; |
| 285 | } | 296 | } |
| 297 | + | ||
| 298 | + private static String vue3VbenTemplatePath(String path) { | ||
| 299 | + return "codegen/vue3_vben/" + path + ".vm"; | ||
| 300 | + } | ||
| 286 | } | 301 | } |
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm
0 → 100644
| 1 | +import { defHttp } from '@/utils/http/axios' | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +// 查询${table.classComment}列表 | ||
| 5 | +export function get${simpleClassName}Page(params) { | ||
| 6 | + return defHttp.get({ url: '${baseURL}/page', params }) | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +// 查询${table.classComment}详情 | ||
| 10 | +export function get${simpleClassName}(id: number) { | ||
| 11 | + return defHttp.get({ url: '${baseURL}/get?id=' + id }) | ||
| 12 | +} | ||
| 13 | + | ||
| 14 | +// 新增${table.classComment} | ||
| 15 | +export function create${simpleClassName}(data) { | ||
| 16 | + return defHttp.post({ url: '${baseURL}/create', data }) | ||
| 17 | +} | ||
| 18 | + | ||
| 19 | +// 修改${table.classComment} | ||
| 20 | +export function update${simpleClassName}(data) { | ||
| 21 | + return defHttp.put({ url: '${baseURL}/update', data }) | ||
| 22 | +} | ||
| 23 | + | ||
| 24 | +// 删除${table.classComment} | ||
| 25 | +export function delete${simpleClassName}(id: number) { | ||
| 26 | + return defHttp.delete({ url: '${baseURL}/delete?id=' + id }) | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +// 导出${table.classComment} Excel | ||
| 30 | +export function export${simpleClassName}(params) { | ||
| 31 | + return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls') | ||
| 32 | +} |
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/data.ts.vm
0 → 100644
| 1 | +import { BasicColumn, FormSchema, useRender } from '@/components/Table' | ||
| 2 | +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||
| 3 | + | ||
| 4 | +export const columns: BasicColumn[] = [ | ||
| 5 | +#foreach($column in $columns) | ||
| 6 | +#if ($column.listOperationResult) | ||
| 7 | + #set ($dictType=$column.dictType) | ||
| 8 | + #set ($javaField = $column.javaField) | ||
| 9 | + #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) | ||
| 10 | + #set ($comment=$column.columnComment) | ||
| 11 | +#if ($column.javaType == "LocalDateTime")## 时间类型 | ||
| 12 | + { | ||
| 13 | + title: '${comment}', | ||
| 14 | + dataIndex: '${javaField}', | ||
| 15 | + width: 180, | ||
| 16 | + customRender: ({ text }) => { | ||
| 17 | + return useRender.renderDate(text) | ||
| 18 | + } | ||
| 19 | + }, | ||
| 20 | +#elseif("" != $column.dictType)## 数据字典 | ||
| 21 | + { | ||
| 22 | + title: '${comment}', | ||
| 23 | + dataIndex: '${javaField}', | ||
| 24 | + width: 180, | ||
| 25 | + customRender: ({ text }) => { | ||
| 26 | + return useRender.renderDict(text, DICT_TYPE.$dictType.toUpperCase()) | ||
| 27 | + } | ||
| 28 | + }, | ||
| 29 | +#else | ||
| 30 | + { | ||
| 31 | + title: '${comment}', | ||
| 32 | + dataIndex: '${javaField}', | ||
| 33 | + width: 160 | ||
| 34 | + }, | ||
| 35 | +#end | ||
| 36 | +#end | ||
| 37 | +#end | ||
| 38 | +] | ||
| 39 | + | ||
| 40 | +export const searchFormSchema: FormSchema[] = [ | ||
| 41 | +#foreach($column in $columns) | ||
| 42 | +#if ($column.listOperation) | ||
| 43 | + #set ($dictType=$column.dictType) | ||
| 44 | + #set ($javaField = $column.javaField) | ||
| 45 | + #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) | ||
| 46 | + #set ($comment=$column.columnComment) | ||
| 47 | + { | ||
| 48 | + label: '${comment}', | ||
| 49 | + field: '${javaField}', | ||
| 50 | + #if ($column.htmlType == "input") | ||
| 51 | + component: 'Input', | ||
| 52 | + #elseif ($column.htmlType == "select" || $column.htmlType == "radio") | ||
| 53 | + component: 'Select', | ||
| 54 | + componentProps: { | ||
| 55 | + #if ("" != $dictType)## 设置了 dictType 数据字典的情况 | ||
| 56 | + options: getIntDictOptions(DICT_TYPE.$dictType.toUpperCase()) | ||
| 57 | + #else## 未设置 dictType 数据字典的情况 | ||
| 58 | + options: [] | ||
| 59 | + #end | ||
| 60 | + }, | ||
| 61 | + #elseif($column.htmlType == "datetime") | ||
| 62 | + component: 'RangePicker', | ||
| 63 | + #end | ||
| 64 | + colProps: { span: 8 } | ||
| 65 | + }, | ||
| 66 | +#end | ||
| 67 | +#end | ||
| 68 | +] | ||
| 69 | + | ||
| 70 | +export const formSchema: FormSchema[] = [ | ||
| 71 | + { | ||
| 72 | + label: '编号', | ||
| 73 | + field: 'id', | ||
| 74 | + show: false, | ||
| 75 | + component: 'Input' | ||
| 76 | + }, | ||
| 77 | +#foreach($column in $columns) | ||
| 78 | +#if ($column.createOperation || $column.updateOperation) | ||
| 79 | + #set ($dictType = $column.dictType) | ||
| 80 | + #set ($javaField = $column.javaField) | ||
| 81 | + #set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) | ||
| 82 | + #set ($comment = $column.columnComment) | ||
| 83 | +#if (!$column.primaryKey)## 忽略主键,不用在表单里 | ||
| 84 | + { | ||
| 85 | + label: '${comment}', | ||
| 86 | + field: '${javaField}', | ||
| 87 | + #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键 | ||
| 88 | + required: true, | ||
| 89 | + #end | ||
| 90 | + #if ($column.htmlType == "input") | ||
| 91 | + component: 'Input' | ||
| 92 | + #elseif($column.htmlType == "imageUpload")## 图片上传 | ||
| 93 | + component: 'Upload' | ||
| 94 | + #elseif($column.htmlType == "fileUpload")## 文件上传 | ||
| 95 | + component: 'Upload' | ||
| 96 | + #elseif($column.htmlType == "editor")## 文本编辑器 | ||
| 97 | + component: 'InputTextArea' | ||
| 98 | + #elseif($column.htmlType == "select")## 下拉框 | ||
| 99 | + component: 'Select', | ||
| 100 | + componentProps: { | ||
| 101 | + #if ("" != $dictType)## 有数据字典 | ||
| 102 | + options: getIntDictOptions(DICT_TYPE.COMMON_STATUS) | ||
| 103 | + #else##没数据字典 | ||
| 104 | + options:[] | ||
| 105 | + #end | ||
| 106 | + } | ||
| 107 | + #elseif($column.htmlType == "checkbox")## 多选框 | ||
| 108 | + component: 'Checkbox', | ||
| 109 | + componentProps: { | ||
| 110 | + #if ("" != $dictType)## 有数据字典 | ||
| 111 | + options: getIntDictOptions(DICT_TYPE.COMMON_STATUS) | ||
| 112 | + #else##没数据字典 | ||
| 113 | + options:[] | ||
| 114 | + #end | ||
| 115 | + } | ||
| 116 | + #elseif($column.htmlType == "radio")## 单选框 | ||
| 117 | + component: 'Radio', | ||
| 118 | + componentProps: { | ||
| 119 | + #if ("" != $dictType)## 有数据字典 | ||
| 120 | + options: getIntDictOptions(DICT_TYPE.COMMON_STATUS) | ||
| 121 | + #else##没数据字典 | ||
| 122 | + options:[] | ||
| 123 | + #end | ||
| 124 | + } | ||
| 125 | + #elseif($column.htmlType == "datetime")## 时间框 | ||
| 126 | + component: 'DatePicker' | ||
| 127 | + #elseif($column.htmlType == "textarea")## 文本域 | ||
| 128 | + component: 'InputTextArea' | ||
| 129 | + #end | ||
| 130 | + }, | ||
| 131 | +#end | ||
| 132 | +#end | ||
| 133 | +#end | ||
| 134 | +] |
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/form.vue.vm
0 → 100644
| 1 | +<template> | ||
| 2 | + <BasicModal v-bind="$attrs" @register="registerModal" :title="isUpdate ? '编辑' : '新增'" @ok="handleSubmit"> | ||
| 3 | + <BasicForm @register="registerForm" /> | ||
| 4 | + </BasicModal> | ||
| 5 | +</template> | ||
| 6 | +<script lang="ts" setup name="${table.className}Modal"> | ||
| 7 | + import { ref, unref } from 'vue' | ||
| 8 | + import { BasicModal, useModalInner } from '@/components/Modal' | ||
| 9 | + import { BasicForm, useForm } from '@/components/Form' | ||
| 10 | + import { formSchema } from './${classNameVar}.data' | ||
| 11 | + import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '@/api/${table.moduleName}/${classNameVar}' | ||
| 12 | + | ||
| 13 | + const emit = defineEmits(['success', 'register']) | ||
| 14 | + const isUpdate = ref(true) | ||
| 15 | + | ||
| 16 | + const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({ | ||
| 17 | + labelWidth: 120, | ||
| 18 | + baseColProps: { span: 24 }, | ||
| 19 | + schemas: formSchema, | ||
| 20 | + showActionButtonGroup: false, | ||
| 21 | + actionColOptions: { span: 23 } | ||
| 22 | + }) | ||
| 23 | + | ||
| 24 | + const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { | ||
| 25 | + resetFields() | ||
| 26 | + setModalProps({ confirmLoading: false }) | ||
| 27 | + isUpdate.value = !!data?.isUpdate | ||
| 28 | + if (unref(isUpdate)) { | ||
| 29 | + const res = await get${simpleClassName}(data.record.id) | ||
| 30 | + setFieldsValue({ ...res }) | ||
| 31 | + } | ||
| 32 | + }) | ||
| 33 | + | ||
| 34 | + async function handleSubmit() { | ||
| 35 | + try { | ||
| 36 | + const values = await validate() | ||
| 37 | + setModalProps({ confirmLoading: true }) | ||
| 38 | + if (unref(isUpdate)) { | ||
| 39 | + await update${simpleClassName}(values) | ||
| 40 | + } else { | ||
| 41 | + await create${simpleClassName}(values) | ||
| 42 | + } | ||
| 43 | + closeModal() | ||
| 44 | + emit('success') | ||
| 45 | + } finally { | ||
| 46 | + setModalProps({ confirmLoading: false }) | ||
| 47 | + } | ||
| 48 | + } | ||
| 49 | +</script> |
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm
0 → 100644
| 1 | +<template> | ||
| 2 | + <div> | ||
| 3 | + <BasicTable @register="registerTable"> | ||
| 4 | + <template #toolbar> | ||
| 5 | + <a-button type="primary" v-auth="['${permissionPrefix}:create']" :preIcon="IconEnum.ADD" @click="handleCreate"> | ||
| 6 | + {{ t('action.create') }} | ||
| 7 | + </a-button> | ||
| 8 | + <a-button type="warning" v-auth="['${permissionPrefix}:export']" :preIcon="IconEnum.EXPORT" @click="handleExport"> | ||
| 9 | + {{ t('action.export') }} | ||
| 10 | + </a-button> | ||
| 11 | + </template> | ||
| 12 | + <template #bodyCell="{ column, record }"> | ||
| 13 | + <template v-if="column.key === 'action'"> | ||
| 14 | + <TableAction | ||
| 15 | + :actions="[ | ||
| 16 | + { icon: IconEnum.EDIT, label: t('action.edit'), auth: '${permissionPrefix}:update', onClick: handleEdit.bind(null, record) }, | ||
| 17 | + { | ||
| 18 | + icon: IconEnum.DELETE, | ||
| 19 | + color: 'error', | ||
| 20 | + label: t('action.delete'), | ||
| 21 | + auth: '${permissionPrefix}:delete', | ||
| 22 | + popConfirm: { | ||
| 23 | + title: t('common.delMessage'), | ||
| 24 | + placement: 'left', | ||
| 25 | + confirm: handleDelete.bind(null, record) | ||
| 26 | + } | ||
| 27 | + } | ||
| 28 | + ]" | ||
| 29 | + /> | ||
| 30 | + </template> | ||
| 31 | + </template> | ||
| 32 | + </BasicTable> | ||
| 33 | + <${simpleClassName}Modal @register="registerModal" @success="reload()" /> | ||
| 34 | + </div> | ||
| 35 | +</template> | ||
| 36 | +<script lang="ts" setup name="${table.className}"> | ||
| 37 | + import { useI18n } from '@/hooks/web/useI18n' | ||
| 38 | + import { useMessage } from '@/hooks/web/useMessage' | ||
| 39 | + import { useModal } from '@/components/Modal' | ||
| 40 | + import ${simpleClassName}Modal from './${simpleClassName}Modal.vue' | ||
| 41 | + import { IconEnum } from '@/enums/appEnum' | ||
| 42 | + import { BasicTable, useTable, TableAction } from '@/components/Table' | ||
| 43 | + import { delete${simpleClassName}, export${simpleClassName}, get${simpleClassName}Page } from '@/api/${table.moduleName}/${classNameVar}' | ||
| 44 | + import { columns, searchFormSchema } from './${classNameVar}.data' | ||
| 45 | + | ||
| 46 | + const { t } = useI18n() | ||
| 47 | + const { createConfirm, createMessage } = useMessage() | ||
| 48 | + const [registerModal, { openModal }] = useModal() | ||
| 49 | + | ||
| 50 | + const [registerTable, { getForm, reload }] = useTable({ | ||
| 51 | + title: '${table.classComment}列表', | ||
| 52 | + api: get${simpleClassName}Page, | ||
| 53 | + columns, | ||
| 54 | + formConfig: { labelWidth: 120, schemas: searchFormSchema }, | ||
| 55 | + useSearchForm: true, | ||
| 56 | + showTableSetting: true, | ||
| 57 | + actionColumn: { | ||
| 58 | + width: 140, | ||
| 59 | + title: t('common.action'), | ||
| 60 | + dataIndex: 'action', | ||
| 61 | + fixed: 'right' | ||
| 62 | + } | ||
| 63 | + }) | ||
| 64 | + | ||
| 65 | + function handleCreate() { | ||
| 66 | + openModal(true, { isUpdate: false }) | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + function handleEdit(record: Recordable) { | ||
| 70 | + openModal(true, { record, isUpdate: true }) | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + async function handleExport() { | ||
| 74 | + createConfirm({ | ||
| 75 | + title: t('common.exportTitle'), | ||
| 76 | + iconType: 'warning', | ||
| 77 | + content: t('common.exportMessage'), | ||
| 78 | + async onOk() { | ||
| 79 | + await export${simpleClassName}(getForm().getFieldsValue()) | ||
| 80 | + createMessage.success(t('common.exportSuccessText')) | ||
| 81 | + } | ||
| 82 | + }) | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + async function handleDelete(record: Recordable) { | ||
| 86 | + await delete${simpleClassName}(record.id) | ||
| 87 | + createMessage.success(t('common.delSuccessText')) | ||
| 88 | + reload() | ||
| 89 | + } | ||
| 90 | +</script> |