mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-10-07 02:01:37 +00:00
refactor(代码生成): 简化代码生成模块配置项和模板逻辑
移除不必要的配置字段如作者、模板类型等,优化字段初始化逻辑 重构模板生成逻辑,移除主子表和树表模板支持 优化前端代码生成页面,简化配置步骤和表单验证 更新文档中的代码生成模块说明和快速开始指南
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
<el-form-item label="表名称" prop="table_name">
|
||||
<el-input v-model="queryFormData.table_name" placeholder="请输入表名称" clearable style="width: 200px" @keyup.enter="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="表描述" prop="tableComment">
|
||||
<el-form-item label="表描述" prop="table_comment">
|
||||
<el-input v-model="queryFormData.table_comment" placeholder="请输入表描述" clearable style="width: 200px" @keyup.enter="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="isExpand" prop="start_time" label="创建时间">
|
||||
@@ -117,7 +117,7 @@
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'updated_at')?.show" label="更新时间" prop="updated_at" />
|
||||
<el-table-column v-if="tableColumns.find(col => col.prop === 'operation')?.show" label="操作" align="center" min-width="120" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button v-hasPerm="['generator:gencode:update']" link type="primary" :icon="MagicStick" @click="handleEditTable(scope.row)">代码生成</el-button>
|
||||
<el-button v-hasPerm="['generator:gencode:update']" link type="primary" :icon="MagicStick" @click="handlePreviewTable(scope.row)">代码生成</el-button>
|
||||
<el-button v-hasPerm="['generator:gencode:delete']" link type="danger" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
<el-button v-hasPerm="['generator:gencode:sync']" link type="success" icon="Refresh" @click="handleSynchDb(scope.row)">同步</el-button>
|
||||
</template>
|
||||
@@ -223,20 +223,15 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="表描述" prop="tableComment">
|
||||
<el-form-item label="表描述" prop="table_comment">
|
||||
<el-input v-model="info.table_comment" placeholder="请输入表描述"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="实体类名称" prop="className">
|
||||
<el-form-item label="实体类名称" prop="class_name">
|
||||
<el-input v-model="info.class_name" placeholder="请输入"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="作者" prop="functionAuthor">
|
||||
<el-input v-model="info.function_author" placeholder="请输入"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="description">
|
||||
<el-input v-model="info.description" type="textarea" :rows="3"></el-input>
|
||||
@@ -394,7 +389,7 @@
|
||||
v-for="dict in dictOptions"
|
||||
:key="dict.dict_type"
|
||||
:label="dict.dict_name"
|
||||
:value="dict.dict_type">
|
||||
:value="dict.dict_type || ''">
|
||||
<span style="float: left">{{ dict.dict_name }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ dict.dict_type }}</span>
|
||||
</el-option>
|
||||
@@ -465,27 +460,6 @@
|
||||
<!-- 第四步:代码生成 -->
|
||||
<el-form v-show="activeStep == 3" ref="genInfo" :model="info" :rules="rules" label-width="150px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="tpl_category">
|
||||
<template #label>生成模板</template>
|
||||
<el-select v-model="info.tpl_category" @change="tplSelectChange">
|
||||
<el-option label="单表(增删改查)" value="crud" />
|
||||
<el-option label="树表(增删改查)" value="tree" />
|
||||
<el-option label="主子表(增删改查)" value="sub" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="tpl_web_type">
|
||||
<template #label>前端类型</template>
|
||||
<el-select v-model="info.tpl_web_type" default-first-option>
|
||||
<el-option label="Vue3 Element Plus 模版" value="element-plus" />
|
||||
<el-option label="Vue2 Element UI 模版" value="element-ui" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="package_name">
|
||||
<template #label>
|
||||
@@ -543,7 +517,7 @@
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-radio v-model="info.gen_type" value="0">zip压缩包</el-radio>
|
||||
<el-radio v-model="info.gen_type" value="1">自定义路径</el-radio>
|
||||
<el-radio v-model="info.gen_type" value="1">项目目录</el-radio>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
@@ -558,142 +532,15 @@
|
||||
<el-tree-select
|
||||
v-model="info.parent_menu_id"
|
||||
:data="menuOptions"
|
||||
:props="{ value: 'menu_id', label: 'menu_name', children: 'children' }"
|
||||
value-key="menu_id"
|
||||
placeholder="请选择系统菜单"
|
||||
check-strictly
|
||||
filterable
|
||||
:render-after-expand="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col v-if="info.gen_type == '1'" :span="24">
|
||||
<el-form-item prop="gen_path">
|
||||
<template #label>
|
||||
自定义路径
|
||||
<el-tooltip content="填写磁盘绝对路径,若不填写,则生成到当前Web项目下" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-input v-model="info.gen_path">
|
||||
<template #append>
|
||||
<el-dropdown>
|
||||
<el-button type="primary">
|
||||
最近路径快速选择
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="handleResetGenPath">恢复默认的生成基础路径</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<template v-if="info.tpl_category == 'tree'">
|
||||
<h4 class="form-header">其他信息</h4>
|
||||
<el-row v-show="info.tpl_category == 'tree'">
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
树编码字段
|
||||
<el-tooltip content="树显示的编码字段名, 如:dept_id" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-select v-model="info.tree_code" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="(column, index) in info.columns || []"
|
||||
:key="index"
|
||||
:label="column.column_name + ':' + column.column_comment"
|
||||
:value="column.column_name"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
树父编码字段
|
||||
<el-tooltip content="树显示的父编码字段名, 如:parent_Id" placement="top">
|
||||
<el-icon><QuestionFilled/></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-select v-model="info.tree_parent_code" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="(column, index) in info.columns || []"
|
||||
:key="index"
|
||||
:label="column.column_name + ':' + column.column_comment"
|
||||
:value="column.column_name"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
树名称字段
|
||||
<el-tooltip content="树节点的显示名称字段名, 如:dept_name" placement="top">
|
||||
<el-icon><QuestionFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-select v-model="info.tree_name" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="(column, index) in info.columns || []"
|
||||
:key="index"
|
||||
:label="column.column_name + ':' + column.column_comment"
|
||||
:value="column.column_name"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<template v-if="info.tpl_category == 'sub'">
|
||||
<h4 class="form-header">关联信息</h4>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
关联子表的表名
|
||||
<el-tooltip content="关联子表的表名, 如:sys_user" placement="top">
|
||||
<el-icon><QuestionFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-select v-model="info.sub_table_name" placeholder="请选择" @change="subSelectChange">
|
||||
<el-option
|
||||
v-for="(item, index) in tables"
|
||||
:key="index"
|
||||
:label="item.table_name + ':' + item.table_comment"
|
||||
:value="item.table_name"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
子表关联的外键名
|
||||
<el-tooltip content="子表关联的外键名, 如:user_id" placement="top">
|
||||
<el-icon><QuestionFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-select v-model="info.sub_table_fk_name" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="(column, index) in subColumns"
|
||||
:key="index"
|
||||
:label="column.column_name + ':' + column.column_comment"
|
||||
:value="column.column_name"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
@@ -721,8 +568,8 @@
|
||||
|
||||
<!-- 第四步:有“上一步”和“写入本地” -->
|
||||
<el-button v-if="activeStep === 3" type="success" :icon="View" @click="prevStep">上一步,预览代码</el-button>
|
||||
<el-button v-if="activeStep === 3" type="warning" :icon="Download" :loading="loading" @click="prepareGenTable(info)">下载代码</el-button>
|
||||
<el-button v-if="activeStep === 3" type="primary" :icon="FolderOpened" :loading="loading" @click="prepareGenTable(info)">写入本地</el-button>
|
||||
<el-button v-if="activeStep === 3" :disabled="info.gen_type != '0'" type="warning" :icon="Download" :loading="loading" @click="handleGenTable(info)">下载代码</el-button>
|
||||
<el-button v-if="activeStep === 3" :disabled="info.gen_type != '1'" type="primary" :icon="FolderOpened" :loading="loading" @click="handleGenTable(info)">写入本地</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
@@ -743,9 +590,12 @@ import type { EditorConfiguration } from "codemirror";
|
||||
import type { CmComponentRef } from "codemirror-editor-vue3";
|
||||
import { ElMessage, ElMessageBox, type FormInstance, type TableInstance } from 'element-plus';
|
||||
import { QuestionFilled, MagicStick, View, CopyDocument, Close, Right, FolderOpened, Back, Download, Edit } from '@element-plus/icons-vue';
|
||||
import GencodeAPI, { type GenTableOutVO, type DatabaseTable, type GenTableQueryParam, type GenTableColumnOutSchema, type GenTableSchema, type DictOption } from "@/api/generator/gencode";
|
||||
import GencodeAPI, { type GenTableOutVO, type DatabaseTable, type GenTableQueryParam, type GenTableColumnOutSchema, type GenTableSchema } from "@/api/generator/gencode";
|
||||
import { formatToDateTime } from "@/utils/dateUtil";
|
||||
|
||||
import MenuAPI, { MenuTable } from "@/api/system/menu";
|
||||
import DictAPI, { DictTable } from "@/api/system/dict";
|
||||
import { formatTree } from "@/utils/common";
|
||||
import { MenuTypeEnum } from "@/enums";
|
||||
|
||||
// 表格列配置接口
|
||||
interface TableColumn {
|
||||
@@ -770,12 +620,6 @@ interface FileData {
|
||||
content: string;
|
||||
}
|
||||
|
||||
// 菜单选项接口
|
||||
interface MenuOption {
|
||||
id: number;
|
||||
menu_name: string;
|
||||
}
|
||||
|
||||
// 组件引用
|
||||
const queryRef = ref<FormInstance>();
|
||||
const table = ref<TableInstance>();
|
||||
@@ -817,9 +661,8 @@ const importQueryFormData = reactive<GenTableQueryParam>({
|
||||
});
|
||||
|
||||
// 下拉选项数据
|
||||
const dictOptions = ref<DictOption[]>([]);
|
||||
const menuOptions = ref<MenuOption[]>([]);
|
||||
const subColumns = ref<GenTableColumnOutSchema[]>([]);
|
||||
const dictOptions = ref<DictTable[]>([]);
|
||||
const menuOptions = ref<OptionType[]>([]);
|
||||
|
||||
// 表格数据
|
||||
type TableItem = {
|
||||
@@ -907,8 +750,8 @@ const filteredTreeData = computed<TreeNode[]>(() => {
|
||||
// scope 过滤:根据路径初步判断
|
||||
if (previewScope.value !== "all") {
|
||||
// 根据后端返回的格式,检查路径或文件名特征
|
||||
const isPythonBackend = parentPath.some(part => part === 'python') || label.includes('.py');
|
||||
const isVueFrontend = parentPath.some(part => part === 'vue') || label.includes('.vue') || label.includes('.ts');
|
||||
const isPythonBackend = parentPath.some(part => part === 'backend' || part === 'python') || label.includes('.py');
|
||||
const isVueFrontend = parentPath.some(part => part === 'frontend' || part === 'vue') || label.includes('.vue') || label.includes('.ts');
|
||||
const isSqlDatabase = parentPath.some(part => part === 'sql') || label.includes('.sql');
|
||||
|
||||
if (previewScope.value === 'backend' && !isPythonBackend) return false;
|
||||
@@ -917,10 +760,10 @@ const filteredTreeData = computed<TreeNode[]>(() => {
|
||||
}
|
||||
|
||||
// 类型过滤:根据文件内容特征判断类型
|
||||
if (label.includes('.py')) return previewTypes.value.includes('python');
|
||||
if (label.includes('.sql')) return previewTypes.value.includes('sql');
|
||||
if (label.includes('.vue')) return previewTypes.value.includes('vue');
|
||||
if (label.includes('.ts')) return previewTypes.value.includes('ts');
|
||||
if (label.endsWith('.py')) return previewTypes.value.includes('python');
|
||||
if (label.endsWith('.sql')) return previewTypes.value.includes('sql');
|
||||
if (label.endsWith('.vue')) return previewTypes.value.includes('vue');
|
||||
if (label.endsWith('.ts')) return previewTypes.value.includes('ts');
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -1192,8 +1035,18 @@ function calculateTableHeight() {
|
||||
tableHeight.value = 680;
|
||||
}
|
||||
|
||||
// 修改菜单选项过滤逻辑,添加递归过滤函数
|
||||
const filterMenuTypes = (nodes: MenuTable[]) => {
|
||||
return nodes
|
||||
.filter(node => node.type === MenuTypeEnum.CATALOG || node.type === MenuTypeEnum.MENU)
|
||||
.map((node: any): any => ({
|
||||
...node,
|
||||
children: node.children ? filterMenuTypes(node.children) : []
|
||||
}));
|
||||
};
|
||||
|
||||
/** 表格行内修改按钮操作 */
|
||||
async function handleEditTable(row?: GenTableOutVO): Promise<void> {
|
||||
async function handlePreviewTable(row?: GenTableOutVO): Promise<void> {
|
||||
const selectedTableId = row?.id || ids.value[0];
|
||||
if (selectedTableId) {
|
||||
// 设置编辑的表ID和名称
|
||||
@@ -1201,7 +1054,12 @@ async function handleEditTable(row?: GenTableOutVO): Promise<void> {
|
||||
// 加载表详情数据
|
||||
await loadTableDetail(selectedTableId);
|
||||
editVisible.value = true;
|
||||
|
||||
|
||||
const menu_response = await MenuAPI.getMenuList();
|
||||
menuOptions.value = formatTree(filterMenuTypes(menu_response.data.data));
|
||||
|
||||
const dict_response = await DictAPI.getDictTypeList({page_no: 1, page_size: 100});
|
||||
dictOptions.value = dict_response.data.data.items;
|
||||
// 延迟计算表格高度,确保DOM已经渲染完成
|
||||
setTimeout(() => {
|
||||
calculateTableHeight();
|
||||
@@ -1356,26 +1214,14 @@ const info = reactive<GenTableOutVO>({
|
||||
business_name: '',
|
||||
function_name: '',
|
||||
gen_type: '0',
|
||||
gen_path: '',
|
||||
parent_menu_id: undefined,
|
||||
function_author: '',
|
||||
description: '',
|
||||
columns: [],
|
||||
tpl_category: 'crud',
|
||||
tpl_web_type: 'element-plus',
|
||||
tree_code: '',
|
||||
tree_parent_code: '',
|
||||
tree_name: '',
|
||||
sub_table_name: '',
|
||||
sub_table_fk_name: '',
|
||||
sub: false,
|
||||
tree: false,
|
||||
crud: true,
|
||||
params: {
|
||||
parent_menu_id: undefined,
|
||||
tree_code: '',
|
||||
tree_name: '',
|
||||
tree_parent_code: ''
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1383,67 +1229,14 @@ const info = reactive<GenTableOutVO>({
|
||||
const rules = {
|
||||
table_name: [{ required: true, message: '表名称不能为空', trigger: 'blur' }],
|
||||
class_name: [{ required: true, message: '实体名称不能为空', trigger: 'blur' }],
|
||||
function_author: [{ required: true, message: '作者不能为空', trigger: 'blur' }],
|
||||
package_name: [{ required: true, message: '生成包路径不能为空', trigger: 'blur' }],
|
||||
module_name: [{ required: true, message: '生成模块名不能为空', trigger: 'blur' }],
|
||||
business_name: [{ required: true, message: '生成业务名不能为空', trigger: 'blur' }],
|
||||
function_name: [{ required: true, message: '生成功能名不能为空', trigger: 'blur' }],
|
||||
parent_menu_id: [{ required: true, message: '所属菜单不能为空', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// ===== 工具函数
|
||||
/** 生成模板变更处理 */
|
||||
function tplSelectChange(): void {
|
||||
// 当选择树表模板时,重置树相关字段
|
||||
if (info.tpl_category === 'tree') {
|
||||
info.tree_code = '';
|
||||
info.tree_parent_code = '';
|
||||
info.tree_name = '';
|
||||
}
|
||||
|
||||
// 当选择主子表模板时,重置子表相关字段
|
||||
if (info.tpl_category === 'sub') {
|
||||
info.sub_table_name = '';
|
||||
info.sub_table_fk_name = '';
|
||||
subColumns.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
/** 子表选择变更处理 */
|
||||
async function subSelectChange(): Promise<void> {
|
||||
if (!info.sub_table_name) {
|
||||
subColumns.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
loading.value = true;
|
||||
// 搜索指定表名的表信息
|
||||
const response = await GencodeAPI.listTable({
|
||||
table_name: info.sub_table_name,
|
||||
page_no: 1,
|
||||
page_size: 10
|
||||
});
|
||||
|
||||
if (response?.data?.data?.items && response.data.data.items.length > 0) {
|
||||
const tableId = response.data.data.items[0].id;
|
||||
if (tableId) {
|
||||
// 获取表详情来获取字段信息
|
||||
const detailResponse = await GencodeAPI.getGenTableDetail(tableId);
|
||||
if (detailResponse?.data?.data?.rows) {
|
||||
subColumns.value = detailResponse.data.data.rows;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取子表字段失败:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 恢复默认生成路径 */
|
||||
function handleResetGenPath(): void {
|
||||
// 这里可以根据实际情况设置默认路径
|
||||
info.gen_path = '';
|
||||
}
|
||||
|
||||
/** 提交表单 - 保存配置 */
|
||||
async function submitForm() {
|
||||
|
||||
@@ -1468,11 +1261,22 @@ async function submitForm() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置params参数
|
||||
if (info.parent_menu_id) {
|
||||
info.params = {
|
||||
parent_menu_id: info.parent_menu_id
|
||||
};
|
||||
}
|
||||
|
||||
// 提交表单数据,确保columns是必需的
|
||||
const tableData = {
|
||||
...info,
|
||||
columns: info.columns || [] // 确保columns存在
|
||||
};
|
||||
|
||||
// 清理不需要的字段
|
||||
delete (tableData as any).params;
|
||||
delete (tableData as any).parent_menu_id;
|
||||
const response = await GencodeAPI.updateTable(tableData as GenTableSchema, info.id || 0);
|
||||
|
||||
if (response?.data?.code === 200) {
|
||||
@@ -1488,27 +1292,6 @@ async function submitForm() {
|
||||
}
|
||||
|
||||
|
||||
/** 关闭抽屉 */
|
||||
// 在生成代码前保存配置并处理菜单和字典信息
|
||||
async function prepareGenTable(info: GenTableOutVO) {
|
||||
try {
|
||||
// 先保存配置
|
||||
const saved = await submitForm();
|
||||
if (!saved) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用handleGenTable执行实际的代码生成
|
||||
await handleGenTable(info);
|
||||
|
||||
// 生成成功后关闭抽屉
|
||||
editVisible.value = false;
|
||||
} catch (error) {
|
||||
console.error('准备生成代码失败:', error);
|
||||
ElMessage.error('操作失败,请稍后重试');
|
||||
}
|
||||
}
|
||||
|
||||
// 下一步
|
||||
async function nextStep(): Promise<void> {
|
||||
if (activeStep.value < 3) {
|
||||
@@ -1585,11 +1368,6 @@ async function loadTableDetail(id: number | string) {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是主子表,加载子表字段
|
||||
if (info.tpl_category === 'sub' && info.sub_table_name) {
|
||||
await subSelectChange();
|
||||
}
|
||||
|
||||
// 重置当前步骤为第一步
|
||||
activeStep.value = 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user