Commit 54efb3428917413945eba05bbb397b88d47508d8

Authored by xingyu
1 parent a57f79cf

feat: add vue3 codegen(preview)

yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
@@ -79,11 +79,20 @@ public class CodegenEngine { @@ -79,11 +79,20 @@ public class CodegenEngine {
79 javaModuleImplTestFilePath("service/${table.businessName}/${table.className}ServiceImplTest")) 79 javaModuleImplTestFilePath("service/${table.businessName}/${table.className}ServiceImplTest"))
80 // Java module-api Main 80 // Java module-api Main
81 .put(javaTemplatePath("enums/errorcode"), javaModuleApiMainFilePath("enums/ErrorCodeConstants_手动操作")) 81 .put(javaTemplatePath("enums/errorcode"), javaModuleApiMainFilePath("enums/ErrorCodeConstants_手动操作"))
82 - // Vue 82 + // Vue2
83 .put(vueTemplatePath("views/index.vue"), 83 .put(vueTemplatePath("views/index.vue"),
84 vueFilePath("views/${table.moduleName}/${classNameVar}/index.vue")) 84 vueFilePath("views/${table.moduleName}/${classNameVar}/index.vue"))
85 .put(vueTemplatePath("api/api.js"), 85 .put(vueTemplatePath("api/api.js"),
86 vueFilePath("api/${table.moduleName}/${classNameVar}.js")) 86 vueFilePath("api/${table.moduleName}/${classNameVar}.js"))
  87 + // Vue3
  88 + .put(vue3TemplatePath("views/index.vue"),
  89 + vue3FilePath("views/${table.moduleName}/${classNameVar}/index.vue"))
  90 + .put(vue3TemplatePath("views/data.ts"),
  91 + vue3FilePath("views/${table.moduleName}/${classNameVar}/${classNameVar}.data.ts"))
  92 + .put(vue3TemplatePath("api/api.ts"),
  93 + vue3FilePath("api/${table.moduleName}/${classNameVar}/index.ts"))
  94 + .put(vue3TemplatePath("api/types.ts"),
  95 + vue3FilePath("api/${table.moduleName}/${classNameVar}/types.ts"))
87 // SQL 96 // SQL
88 .put("codegen/sql/sql.vm", "sql/sql.sql") 97 .put("codegen/sql/sql.vm", "sql/sql.sql")
89 .put("codegen/sql/h2.vm", "sql/h2.sql") 98 .put("codegen/sql/h2.vm", "sql/h2.sql")
@@ -228,5 +237,12 @@ public class CodegenEngine { @@ -228,5 +237,12 @@ public class CodegenEngine {
228 return "yudao-ui-${sceneEnum.basePackage}/" + // 顶级目录 237 return "yudao-ui-${sceneEnum.basePackage}/" + // 顶级目录
229 "src/" + path; 238 "src/" + path;
230 } 239 }
  240 + private static String vue3TemplatePath(String path) {
  241 + return "codegen/vue3/" + path + ".vm";
  242 + }
231 243
  244 + private static String vue3FilePath(String path) {
  245 + return "yudao-ui-${sceneEnum.basePackage}-vue3/" + // 顶级目录
  246 + "src/" + path;
  247 + }
232 } 248 }
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm 0 → 100644
  1 +import { useAxios } from '@/hooks/web/useAxios'
  2 +import { ${simpleClassName}VO,${simpleClassName}PageReqVO,${simpleClassName}ExcelReqVO } from './types'
  3 +
  4 +const request = useAxios()
  5 +
  6 +#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
  7 +// 查询${table.classComment}列表
  8 +export const getPostPageApi = async (params: ${simpleClassName}PageReqVO) => {
  9 + return await request.get({ url: '${baseURL}/page', params })
  10 +}
  11 +
  12 +// 查询${table.classComment}详情
  13 +export const getPostApi = async (id: number) => {
  14 + return await request.get({ url: '${baseURL}/get?id=' + id })
  15 +}
  16 +
  17 +// 新增${table.classComment}
  18 +export const createPostApi = async (data: ${simpleClassName}VO) => {
  19 + return await request.post({ url: '${baseURL}/create', data })
  20 +}
  21 +
  22 +// 修改${table.classComment}
  23 +export const updatePostApi = async (data: ${simpleClassName}VO) => {
  24 + return await request.put({ url: '${baseURL}/update', data })
  25 +}
  26 +
  27 +// 删除${table.classComment}
  28 +export const deletePostApi = async (id: number) => {
  29 + return await request.delete({ url: '${baseURL}/delete?id=' + id })
  30 +}
  31 +
  32 +// 导出${table.classComment} Excel
  33 +export const exportPostApi = async (params: ${simpleClassName}ExcelReqVO) => {
  34 + return await request.get({ url: '${baseURL}/export-excel', params, responseType: 'blob' })
  35 +}
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/types.ts.vm 0 → 100644
  1 +export type ${simpleClassName}VO = {
  2 +#foreach ($column in $columns)
  3 +#if ($column.createOperation || $column.updateOperation)
  4 +#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer")
  5 + ${column.javaField}: number
  6 +#else
  7 + ${column.javaField}: ${column.javaType.toLowerCase()}
  8 +#end
  9 +#end
  10 +#end
  11 +}
  12 +
  13 +export type ${simpleClassName}PageReqVO = {
  14 +#foreach ($column in $columns)
  15 +#if (${column.listOperation})##查询操作
  16 +#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer")
  17 + ${column.javaField}: number
  18 +#else
  19 + ${column.javaField}: ${column.javaType.toLowerCase()}
  20 +#end
  21 +#end
  22 +#end
  23 +}
  24 +
  25 +export type ${simpleClassName}ExcelReqVO = {
  26 +#foreach ($column in $columns)
  27 +#if (${column.listOperation})##查询操作
  28 +#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer")
  29 + ${column.javaField}: number
  30 +#else
  31 + ${column.javaField}: ${column.javaType.toLowerCase()}
  32 +#end
  33 +#end
  34 +#end
  35 +}
0 \ No newline at end of file 36 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/data.ts.vm 0 → 100644
  1 +import { reactive } from 'vue'
  2 +import { useI18n } from '@/hooks/web/useI18n'
  3 +import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
  4 +import { DICT_TYPE } from '@/utils/dict'
  5 +const { t } = useI18n() // 国际化
  6 +// 表单校验
  7 +export const rules = reactive({
  8 +#foreach ($column in $columns)
  9 +#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
  10 +#set($comment=$column.columnComment)
  11 + $column.javaField: [{ required: true, message: "${comment}不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }],
  12 +#end
  13 +#end
  14 +})
  15 +// CrudSchema
  16 +const crudSchemas = reactive<CrudSchema[]>([
  17 +#foreach($column in $columns)
  18 + #if ($column.listOperation || $column.listOperationResult || $column.createOperation || $column.updateOperation)
  19 + #set ($dictType = $column.dictType)
  20 + {
  21 + label: '${column.columnComment}',
  22 + field: '${column.javaField}',
  23 + #if ("" != $dictType)## 有数据字典
  24 + dictType: DICT_TYPE.$dictType.toUpperCase(),
  25 + #end
  26 + #if($column.primaryKey)
  27 + type: 'index',
  28 + form: {
  29 + show: false
  30 + },
  31 + detail: {
  32 + show: false
  33 + }
  34 + #else
  35 + #if (!$column.createOperation && !$column.updateOperation)
  36 + form: {
  37 + false
  38 + },
  39 + #elseif(!("" != $column.dictType))
  40 + form: {
  41 + show: true,
  42 + #if ($column.htmlType == "datetime")## 时间框
  43 + component: 'DatePicker',
  44 + componentProps: {
  45 + type: 'datetime',
  46 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
  47 + }
  48 + #elseif($column.htmlType == "editor")## 文本编辑器
  49 + component: 'Editor',
  50 + colProps: {
  51 + span: 24
  52 + },
  53 + componentProps: {
  54 + valueHtml: ''
  55 + }
  56 + #elseif($column.htmlType == "textarea")## 文本框
  57 + component: 'Input',
  58 + componentProps: {
  59 + type: 'textarea',
  60 + rows: 4
  61 + },
  62 + colProps: {
  63 + span: 24
  64 + }
  65 + #end
  66 + },
  67 + #end
  68 + #if ($column.listOperationResult)
  69 + search: {
  70 + #if($column.htmlType == "input")
  71 + show: true
  72 + #else
  73 + #if($column.htmlType == "datetime")
  74 + show: true,
  75 + component: 'DatePicker',
  76 + componentProps: {
  77 + type: 'datetimerange',
  78 + valueFormat: 'YYYY-MM-DD HH:mm:ss'
  79 + }
  80 + #elseif($column.htmlType == "select" || $column.htmlType == "radio")
  81 + #if ("" == $dictType)## 没有数据字典
  82 + show: true,
  83 + component: 'Select',
  84 + componentProps: {
  85 + option: [{'','请选择字典生成'}]
  86 + }
  87 + #else
  88 + show: true
  89 + #end
  90 + #end
  91 + #end
  92 + }
  93 + #end
  94 + #end
  95 + },
  96 + #end
  97 +#end
  98 + {
  99 + label: t('table.action'),
  100 + field: 'action',
  101 + width: '240px',
  102 + form: {
  103 + show: false
  104 + },
  105 + detail: {
  106 + show: false
  107 + }
  108 + }
  109 +])
  110 +
  111 +export const { allSchemas } = useCrudSchemas(crudSchemas)
0 \ No newline at end of file 112 \ No newline at end of file
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm 0 → 100644
  1 +<script setup lang="ts">
  2 + import { ref, unref } from 'vue'
  3 + import dayjs from 'dayjs'
  4 + import { ElMessage } from 'element-plus'
  5 + import { DICT_TYPE } from '@/utils/dict'
  6 + import { useTable } from '@/hooks/web/useTable'
  7 + import { useI18n } from '@/hooks/web/useI18n'
  8 + import { FormExpose } from '@/components/Form'
  9 + import type { ${simpleClassName}VO } from '@/api/system/post/types'
  10 + import { rules, allSchemas } from './post.data'
  11 + import * as ${simpleClassName}Api from '@/api/system/post'
  12 + const { t } = useI18n() // 国际化
  13 +
  14 + // ========== 列表相关 ==========
  15 + const { register, tableObject, methods } = useTable<${simpleClassName}VO>({
  16 + getListApi: ${simpleClassName}Api.get${simpleClassName}PageApi,
  17 + delListApi: ${simpleClassName}Api.delete${simpleClassName}Api,
  18 + exportListApi: ${simpleClassName}Api.export${simpleClassName}Api
  19 + })
  20 + const { getList, setSearchParams, delList, exportList } = methods
  21 +
  22 + // 导出操作
  23 + const handleExport = async () => {
  24 + await exportList('数据.xls')
  25 + }
  26 +
  27 + // ========== CRUD 相关 ==========
  28 + const actionLoading = ref(false) // 遮罩层
  29 + const actionType = ref('') // 操作按钮的类型
  30 + const dialogVisible = ref(false) // 是否显示弹出层
  31 + const dialogTitle = ref('edit') // 弹出层标题
  32 + const formRef = ref<FormExpose>() // 表单 Ref
  33 +
  34 + // 设置标题
  35 + const setDialogTile = (type: string) => {
  36 + dialogTitle.value = t('action.' + type)
  37 + actionType.value = type
  38 + dialogVisible.value = true
  39 + }
  40 +
  41 + // 新增操作
  42 + const handleCreate = () => {
  43 + setDialogTile('create')
  44 + // 重置表单
  45 + unref(formRef)?.getElFormRef()?.resetFields()
  46 + }
  47 +
  48 + // 修改操作
  49 + const handleUpdate = async (row: ${simpleClassName}VO) => {
  50 + setDialogTile('update')
  51 + // 设置数据
  52 + const res = await ${simpleClassName}Api.get${simpleClassName}Api(row.id)
  53 + unref(formRef)?.setValues(res)
  54 + }
  55 +
  56 + // 提交按钮
  57 + const submitForm = async () => {
  58 + actionLoading.value = true
  59 + // 提交请求
  60 + try {
  61 + const data = unref(formRef)?.formModel as ${simpleClassName}VO
  62 + if (actionType.value === 'create') {
  63 + await ${simpleClassName}Api.create${simpleClassName}Api(data)
  64 + ElMessage.success(t('common.createSuccess'))
  65 + } else {
  66 + await ${simpleClassName}Api.update${simpleClassName}Api(data)
  67 + ElMessage.success(t('common.updateSuccess'))
  68 + }
  69 + // 操作成功,重新加载列表
  70 + dialogVisible.value = false
  71 + await getList()
  72 + } finally {
  73 + actionLoading.value = false
  74 + }
  75 + }
  76 +
  77 + // 删除操作
  78 + const handleDelete = (row: ${simpleClassName}VO) => {
  79 + delList(row.id, false)
  80 + }
  81 +
  82 + // ========== 详情相关 ==========
  83 + const detailRef = ref() // 详情 Ref
  84 +
  85 + // 详情操作
  86 + const handleDetail = async (row: ${simpleClassName}VO) => {
  87 + // 设置数据
  88 + detailRef.value = row
  89 + setDialogTile('detail')
  90 + }
  91 +
  92 + // ========== 初始化 ==========
  93 + getList()
  94 +</script>
  95 +
  96 +<template>
  97 + <!-- 搜索工作区 -->
  98 + <ContentWrap>
  99 + <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
  100 + </ContentWrap>
  101 + <ContentWrap>
  102 + <!-- 操作工具栏 -->
  103 + <div class="mb-10px">
  104 + <el-button type="primary" v-hasPermi="['${permissionPrefix}:create']" @click="handleCreate">
  105 + <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
  106 + </el-button>
  107 + <el-button
  108 + type="warning"
  109 + v-hasPermi="['${permissionPrefix}:export']"
  110 + :loading="tableObject.exportLoading"
  111 + @click="handleExport"
  112 + >
  113 + <Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
  114 + </el-button>
  115 + </div>
  116 + <!-- 列表 -->
  117 + <Table
  118 + :columns="allSchemas.tableColumns"
  119 + :selection="false"
  120 + :data="tableObject.tableList"
  121 + :loading="tableObject.loading"
  122 + :pagination="{
  123 + total: tableObject.total
  124 + }"
  125 + v-model:pageSize="tableObject.pageSize"
  126 + v-model:currentPage="tableObject.currentPage"
  127 + @register="register"
  128 + >
  129 +#foreach($column in $columns)
  130 +#if ($column.listOperationResult)
  131 + #set ($dictType=$column.dictType)
  132 + #if ($column.javaType == "Date")## 时间类型
  133 + <template #${column.javaField}="{ row }">
  134 + <span>{{ dayjs(row.${column.javaField}).format('YYYY-MM-DD HH:mm:ss') }}</span>
  135 + </template>
  136 + #elseif("" != $column.dictType)## 数据字典
  137 + <template #${column.javaField}="{ row }">
  138 + <DictTag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${column.javaField}" />
  139 + </template>
  140 + #end
  141 +#end
  142 +#end
  143 + <template #action="{ row }">
  144 + <el-button
  145 + link
  146 + type="primary"
  147 + v-hasPermi="['${permissionPrefix}:update']"
  148 + @click="handleUpdate(row)"
  149 + >
  150 + <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
  151 + </el-button>
  152 + <el-button
  153 + link
  154 + type="primary"
  155 + v-hasPermi="['${permissionPrefix}:update']"
  156 + @click="handleDetail(row)"
  157 + >
  158 + <Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
  159 + </el-button>
  160 + <el-button
  161 + link
  162 + type="primary"
  163 + v-hasPermi="['${permissionPrefix}:delete']"
  164 + @click="handleDelete(row)"
  165 + >
  166 + <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
  167 + </el-button>
  168 + </template>
  169 + </Table>
  170 + </ContentWrap>
  171 +
  172 + <Dialog v-model="dialogVisible" :title="dialogTitle">
  173 + <!-- 对话框(添加 / 修改) -->
  174 + <Form
  175 + v-if="['create', 'update'].includes(actionType)"
  176 + :schema="allSchemas.formSchema"
  177 + :rules="rules"
  178 + ref="formRef"
  179 + />
  180 + <!-- 对话框(详情) -->
  181 + <Descriptions
  182 + v-if="actionType === 'detail'"
  183 + :schema="allSchemas.detailSchema"
  184 + :data="detailRef"
  185 + >
  186 +#foreach($column in $columns)
  187 + #if ($column.listOperationResult)
  188 + #set ($dictType=$column.dictType)
  189 + #if ($column.javaType == "Date")## 时间类型
  190 + <template #${column.javaField}="{ row }">
  191 + <span>{{ dayjs(row.${column.javaField}).format('YYYY-MM-DD HH:mm:ss') }}</span>
  192 + </template>
  193 + #elseif("" != $column.dictType)## 数据字典
  194 + <template #${column.javaField}="{ row }">
  195 + <DictTag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${column.javaField}" />
  196 + </template>
  197 + #end
  198 + #end
  199 +#end
  200 + </Descriptions>
  201 + <!-- 操作按钮 -->
  202 + <template #footer>
  203 + <el-button
  204 + v-if="['create', 'update'].includes(actionType)"
  205 + type="primary"
  206 + :loading="actionLoading"
  207 + @click="submitForm"
  208 + >
  209 + {{ t('action.save') }}
  210 + </el-button>
  211 + <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
  212 + </template>
  213 + </Dialog>
  214 +</template>
yudao-ui-admin/src/views/infra/codegen/index.vue
@@ -98,7 +98,7 @@ hljs.registerLanguage(&quot;html&quot;, require(&quot;highlight.js/lib/languages/xml&quot;)); @@ -98,7 +98,7 @@ hljs.registerLanguage(&quot;html&quot;, require(&quot;highlight.js/lib/languages/xml&quot;));
98 hljs.registerLanguage("vue", require("highlight.js/lib/languages/xml")); 98 hljs.registerLanguage("vue", require("highlight.js/lib/languages/xml"));
99 hljs.registerLanguage("javascript", require("highlight.js/lib/languages/javascript")); 99 hljs.registerLanguage("javascript", require("highlight.js/lib/languages/javascript"));
100 hljs.registerLanguage("sql", require("highlight.js/lib/languages/sql")); 100 hljs.registerLanguage("sql", require("highlight.js/lib/languages/sql"));
101 - 101 +hljs.registerLanguage("typescript", require("highlight.js/lib/languages/typescript"));
102 export default { 102 export default {
103 name: "Codegen", 103 name: "Codegen",
104 components: { importTable }, 104 components: { importTable },