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
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 | 116 | vue3FilePath("views/${table.moduleName}/${classNameVar}/index.vue")) |
| 117 | 117 | .put(CodegenFrontTypeEnum.VUE3_SCHEMA.getType(), vue3SchemaTemplatePath("views/form.vue"), |
| 118 | 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 | 130 | .build(); |
| 120 | 131 | |
| 121 | 132 | @Resource |
| ... | ... | @@ -283,4 +294,8 @@ public class CodegenEngine { |
| 283 | 294 | private static String vue3SchemaTemplatePath(String path) { |
| 284 | 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> | ... | ... |