refactor(gencode): 重构代码生成模块,优化类型定义和组件逻辑

- 将多个Vue组件脚本迁移至TypeScript,增强类型安全
- 重构API接口定义,优化参数传递和响应处理
- 改进表单验证逻辑,简化冗余代码
- 统一使用Element Plus的消息提示组件
- 优化路由处理逻辑,修复混合布局下的路径解析问题
- 完善类型定义,增加GenTableSchema等接口
- 移除未使用的导入和冗余代码
- 改进代码预览和复制功能
This commit is contained in:
zhangtao
2025-10-06 20:17:47 +08:00
parent 75215a89c2
commit f2a3c1f2dd
15 changed files with 1207 additions and 1031 deletions
+2 -2
View File
@@ -287,7 +287,7 @@ class Settings(BaseSettings):
def ASYNC_DB_URI(self) -> str: def ASYNC_DB_URI(self) -> str:
"""获取异步数据库连接""" """获取异步数据库连接"""
if self.DATABASE_TYPE == "mysql": if self.DATABASE_TYPE == "mysql":
return f"mysql+asyncmy://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}?charset='utf8mb4'" return f"mysql+asyncmy://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}?charset=utf8mb4"
elif self.DATABASE_TYPE == "postgresql": elif self.DATABASE_TYPE == "postgresql":
return f"postgresql+asyncpg://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}" return f"postgresql+asyncpg://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
elif self.DATABASE_TYPE == "sqlite": elif self.DATABASE_TYPE == "sqlite":
@@ -299,7 +299,7 @@ class Settings(BaseSettings):
def DB_URI(self) -> str: def DB_URI(self) -> str:
"""获取同步数据库连接""" """获取同步数据库连接"""
if self.DATABASE_TYPE == "mysql": if self.DATABASE_TYPE == "mysql":
return f"mysql+pymysql://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}?charset='utf8mb4'" return f"mysql+pymysql://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}?charset=utf8mb4"
elif self.DATABASE_TYPE == "postgresql": elif self.DATABASE_TYPE == "postgresql":
return f"postgresql+psycopg2://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}" return f"postgresql+psycopg2://{self.DATABASE_USER}:{quote_plus(self.DATABASE_PASSWORD)}@{self.DATABASE_HOST}:{self.DATABASE_PORT}/{self.DATABASE_NAME}"
elif self.DATABASE_TYPE == "sqlite": elif self.DATABASE_TYPE == "sqlite":
File diff suppressed because one or more lines are too long
+24 -16
View File
@@ -26,7 +26,7 @@ const GencodeAPI = {
return request<ApiResponse>({ return request<ApiResponse>({
url: `${API_PATH}/import`, url: `${API_PATH}/import`,
method: 'post', method: 'post',
data: { table_names } params: { table_names }
}) })
}, },
@@ -48,7 +48,7 @@ const GencodeAPI = {
}, },
// 修改代码生成信息 // 修改代码生成信息
updateGenTable(table_id: number, body: GenTableUpdateSchema) { updateGenTable(table_id: number, body: GenTableSchema) {
return request<ApiResponse>({ return request<ApiResponse>({
url: `${API_PATH}/update/${table_id}`, url: `${API_PATH}/update/${table_id}`,
method: 'put', method: 'put',
@@ -57,11 +57,11 @@ const GencodeAPI = {
}, },
// 删除表数据 // 删除表数据
deleteTable(tableIds: number[]) { deleteTable(data: GenTableDeleteSchema) {
return request<ApiResponse>({ return request<ApiResponse>({
url: `${API_PATH}/delete`, url: `${API_PATH}/delete`,
method: 'delete', method: 'delete',
data: tableIds data: data
}) })
}, },
@@ -118,10 +118,6 @@ export interface TablePageQuery extends PageQuery {
table_name?: string; table_name?: string;
/** 表描述 */ /** 表描述 */
table_comment?: string; table_comment?: string;
/** 开始时间 */
start_time?: string;
/** 结束时间 */
end_time?: string;
} }
/** 数据表分页对象 */ /** 数据表分页对象 */
@@ -192,18 +188,18 @@ export interface GenTableOutVO {
crud?: boolean; crud?: boolean;
} }
/** 代码生成表更新模型 */ /** 代码生成业务表模型 */
export interface GenTableUpdateSchema extends GenTableOutVO { export interface GenTableSchema extends GenTableOutVO {
/** 主键信息 */ /** 主键信息 */
pk_column?: GenTableColumnUpdateSchema; pk_column?: GenTableColumnOutSchema;
/** 子表信息 */ /** 子表信息 */
sub_table?: GenTableUpdateSchema; sub_table?: GenTableSchema;
/** 表列信息 */ /** 表列信息 */
columns: GenTableColumnUpdateSchema[]; columns: GenTableColumnOutSchema[];
} }
/** 代码生成表列更新模型 */ /** 代码生成业务表列模型 */
export interface GenTableColumnUpdateSchema { export interface GenTableColumnSchema {
/** 主键 */ /** 主键 */
id?: number; id?: number;
/** 归属表编号 */ /** 归属表编号 */
@@ -242,6 +238,12 @@ export interface GenTableColumnUpdateSchema {
dict_type: string; dict_type: string;
/** 排序 */ /** 排序 */
sort?: number; sort?: number;
/** 功能描述 */
description?: string;
}
/** 代码生成业务表列输出模型 */
export interface GenTableColumnOutSchema extends GenTableColumnSchema {
/** 字段大写形式 */ /** 字段大写形式 */
cap_python_field?: string; cap_python_field?: string;
/** 是否主键 */ /** 是否主键 */
@@ -266,12 +268,18 @@ export interface GenTableColumnUpdateSchema {
usable_column?: boolean; usable_column?: boolean;
} }
/** 删除代码生成业务表模型 */
export interface GenTableDeleteSchema {
/** 需要删除的代码生成业务表ID列表 */
table_ids: number[];
}
/** 表详情查询结果 */ /** 表详情查询结果 */
export interface GenTableDetailResult { export interface GenTableDetailResult {
/** 表信息 */ /** 表信息 */
info: GenTableOutVO; info: GenTableOutVO;
/** 表列信息 */ /** 表列信息 */
rows: GenTableColumnUpdateSchema[]; rows: GenTableColumnOutSchema[];
/** 所有表信息 */ /** 所有表信息 */
tables: GenTableOutVO[]; tables: GenTableOutVO[];
} }
@@ -95,7 +95,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useRoute, useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { DeviceEnum } from "@/enums/settings/device.enum"; import { DeviceEnum } from "@/enums/settings/device.enum";
import { useAppStore, useSettingsStore, useUserStore, useLockStore } from "@/store"; import { useAppStore, useSettingsStore, useUserStore, useLockStore } from "@/store";
@@ -111,7 +111,7 @@ import Notification from "@/components/Notification/index.vue";
import LockDialog from './LockDialog.vue' import LockDialog from './LockDialog.vue'
import LockPage from './LockPage.vue' import LockPage from './LockPage.vue'
import Guide from '@/components/Guide/index.vue' import Guide from '@/components/Guide/index.vue'
import ConfigInfoDrawer from "@/views/system/config/components/ConfigInfoDrawer.vue" import ConfigInfoDrawer from "@/views/system/param/components/ConfigInfoDrawer.vue"
const { t } = useI18n(); const { t } = useI18n();
@@ -119,7 +119,6 @@ const appStore = useAppStore();
const settingStore = useSettingsStore(); const settingStore = useSettingsStore();
const userStore = useUserStore(); const userStore = useUserStore();
const route = useRoute();
const router = useRouter(); const router = useRouter();
// 是否为桌面设备 // 是否为桌面设备
@@ -225,7 +224,6 @@ function logout() {
lockScroll: false, lockScroll: false,
}).then(() => { }).then(() => {
userStore.logout().then(() => { userStore.logout().then(() => {
// router.push(`/login?redirect=${route.fullPath}`);
router.push(`/login`); router.push(`/login`);
}); });
}).catch(() => { }).catch(() => {
@@ -13,7 +13,7 @@
<router-link <router-link
v-for="tag in displayedViews" :key="tag.fullPath" v-for="tag in displayedViews" :key="tag.fullPath"
:class="['tags-item', { active: tagsViewStore.isActive(tag) }]" :to="{ path: tag.path, query: tag.query }" :class="['tags-item', { active: tagsViewStore.isActive(tag) }]" :to="{ path: tag.path, query: tag.query }"
@click="handleTabClick(tag)" @click="router.push({path: tag.fullPath, query: tag.query})"
@click.middle="handleMiddleClick(tag)"> @click.middle="handleMiddleClick(tag)">
<!-- 为所有标签添加右键菜单 --> <!-- 为所有标签添加右键菜单 -->
<el-dropdown <el-dropdown
@@ -343,10 +343,10 @@ const updateCurrentTag = () => {
/** /**
* 处理标签点击 * 处理标签点击
*/ */
const handleTabClick = (tag: TagView) => { // const handleTabClick = (tag: TagView) => {
// 设置标签切换来源为标签容器点击 // // 设置标签切换来源为标签容器点击
tagSwitchSource.value = 'tab'; // tagSwitchSource.value = 'tab';
}; // };
/** /**
* 处理中键点击 * 处理中键点击
@@ -386,13 +386,14 @@ const handleScroll = (event: WheelEvent) => {
* 刷新标签 * 刷新标签
*/ */
const refreshSelectedTag = (tag: TagView | null) => { const refreshSelectedTag = (tag: TagView | null) => {
if (!tag) return;
// 总是使用当前路由对应的标签 // 总是使用当前路由对应的标签
const currentTag = routePathMap.value.get(route.path); // const currentTag = routePathMap.value.get(route.path);
if (!currentTag) return; // if (!currentTag) return;
tagsViewStore.delCachedView(currentTag); tagsViewStore.delCachedView(tag);
nextTick(() => { nextTick(() => {
router.replace("/redirect" + currentTag.fullPath); router.replace("/redirect" + tag.fullPath);
}); });
}; };
@@ -4,7 +4,8 @@ import { useAppStore } from "@/store";
import { DeviceEnum } from "@/enums/settings/device.enum"; import { DeviceEnum } from "@/enums/settings/device.enum";
/** /**
* 布局响应式处理逻辑 * 设备检测和响应式处理
* 监听屏幕尺寸变化,自动调整设备类型和侧边栏状态
*/ */
export function useLayoutResponsive() { export function useLayoutResponsive() {
const appStore = useAppStore(); const appStore = useAppStore();
@@ -13,16 +14,19 @@ export function useLayoutResponsive() {
// 定义响应式断点 // 定义响应式断点
const WIDTH_DESKTOP = 992; // 桌面设备断点 (>=992px) const WIDTH_DESKTOP = 992; // 桌面设备断点 (>=992px)
// 计算设备类型
const isDesktop = computed(() => width.value >= WIDTH_DESKTOP);
const isMobile = computed(() => appStore.device === DeviceEnum.MOBILE);
// 设置当前设备类型并调整侧边栏状态 // 设置当前设备类型并调整侧边栏状态
watchEffect(() => { watchEffect(() => {
const isDesktop = width.value >= WIDTH_DESKTOP; const deviceType = isDesktop.value ? DeviceEnum.DESKTOP : DeviceEnum.MOBILE;
const deviceType = isDesktop ? DeviceEnum.DESKTOP : DeviceEnum.MOBILE;
// 更新设备类型 // 更新设备类型
appStore.toggleDevice(deviceType); appStore.toggleDevice(deviceType);
// 根据设备类型调整侧边栏状态 // 根据设备类型调整侧边栏状态
if (isDesktop) { if (isDesktop.value) {
appStore.openSideBar(); appStore.openSideBar();
} else { } else {
appStore.closeSideBar(); appStore.closeSideBar();
@@ -30,7 +34,7 @@ export function useLayoutResponsive() {
}); });
return { return {
isDesktop: computed(() => width.value >= WIDTH_DESKTOP), isDesktop,
isMobile: computed(() => appStore.device === DeviceEnum.MOBILE), isMobile,
}; };
} }
@@ -28,7 +28,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { useLayout } from "../composables/useLayout"; import { useLayout } from "../composables/useLayout";
import { useLayoutResponsive } from "../composables/useLayoutResponsive"; import { useLayoutResponsive } from "../composables/useLayoutResponsive";
import { defaultSettings } from "@/settings";
import { useSettingsStore } from "@/store"; import { useSettingsStore } from "@/store";
const settingStore = useSettingsStore(); const settingStore = useSettingsStore();
+10 -11
View File
@@ -109,31 +109,30 @@ function resolvePath(routePath: string) {
} }
if (routePath.startsWith("/")) { if (routePath.startsWith("/")) {
return routePath; return activeTopMenuPath.value + routePath;
} }
return `${routePath}`; return `${activeTopMenuPath.value}/${routePath}`;
} }
// 优化后的路由监听逻辑,仅在顶级路径实际变化时更新菜单
let prevTopMenuPath = '';
watch( watch(
() => route.path, () => route.path,
(newPath) => { (newPath) => {
// 获取顶级路径 // 获取顶级路径
const topMenuPath = const topMenuPath = newPath.split("/").filter(Boolean).length > 1 ? newPath.match(/^\/[^/]+/)?.[0] || "/" : "/";
newPath.split("/").filter(Boolean).length > 1 ? newPath.match(/^\/[^/]+/)?.[0] || "/" : "/";
// 如果当前路径属于当前激活的顶部菜单
if (newPath.startsWith(activeTopMenuPath.value)) {
// no-op
}
// 仅在顶级路径实际变化时才执行更新 // 仅在顶级路径实际变化时才执行更新
if (topMenuPath !== prevTopMenuPath) { else if (topMenuPath !== activeTopMenuPath.value) {
prevTopMenuPath = topMenuPath;
// 主动更新顶部菜单和左侧菜单 // 主动更新顶部菜单和左侧菜单
const appStore = useAppStore(); const appStore = useAppStore();
const permissionStore = usePermissionStore(); const permissionStore = usePermissionStore();
appStore.activeTopMenu(topMenuPath); appStore.activeTopMenu(topMenuPath);
permissionStore.updateSideMenu(topMenuPath); permissionStore.setMixLayoutSideMenus(topMenuPath);
} }
}, },
{ immediate: true } { immediate: true }
@@ -30,7 +30,7 @@
</el-form> </el-form>
</template> </template>
<script setup> <script setup lang="ts">
defineProps({ defineProps({
info: { info: {
type: Object, type: Object,
@@ -12,12 +12,12 @@
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup lang="ts">
import GencodeAPI from "@/api/generator/gencode"; import GencodeAPI from "@/api/generator/gencode";
import { ElMessage } from 'element-plus';
const visible = ref(false); const visible = ref(false);
const content = ref(""); const content = ref("");
const { proxy } = getCurrentInstance();
const emit = defineEmits(["ok"]); const emit = defineEmits(["ok"]);
/** 显示弹框 */ /** 显示弹框 */
@@ -28,12 +28,12 @@ function show() {
/** 导入按钮操作 */ /** 导入按钮操作 */
function handleImportTable() { function handleImportTable() {
if (content.value === "") { if (content.value === "") {
proxy.$modal.msgError("请输入建表语句"); ElMessage.error("请输入建表语句");
return; return;
} }
GencodeAPI.createTable({ sql: content.value }).then(res => { GencodeAPI.createTable(content.value).then(res => {
proxy.$modal.msgSuccess(res.msg); ElMessage.success(res.data.msg || "创建成功");
if (res.code === 200) { if (res.data.code === 200) {
visible.value = false; visible.value = false;
emit("ok"); emit("ok");
} }
@@ -122,7 +122,12 @@
</el-table> </el-table>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="生成信息" name="genInfo"> <el-tab-pane label="生成信息" name="genInfo">
<gen-info-form ref="genInfo" :info="info" :tables="tables" /> <!-- 将GenTableSchema类型转换为GenInfo类型 -->
<gen-info-form
ref="genInfo"
:info="convertToGenInfo(info)"
:tables="tables"
/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<el-form label-width="100px"> <el-form label-width="100px">
@@ -134,74 +139,96 @@
</el-card> </el-card>
</template> </template>
<script setup name="GenEdit"> <script setup lang="ts" name="GenEdit">
import GencodeAPI from "@/api/generator/gencode"; import GencodeAPI from "@/api/generator/gencode";
import DictAPI from "@/api/system/dict"; import DictAPI from "@/api/system/dict";
import basicInfoForm from "./components/basicInfoForm"; import { ElMessage } from 'element-plus';
import genInfoForm from "./components/genInfoForm"; import router from '@/router';
import type { GenTableSchema, GenTableDetailResult } from '@/api/generator/gencode';
const route = useRoute(); const route = useRoute();
const { proxy } = getCurrentInstance(); const basicInfoRef = ref();
const genInfoRef = ref();
const activeName = ref("columnInfo"); const activeName = ref("columnInfo");
const tableHeight = ref(document.documentElement.scrollHeight - 245 + "px"); const tableHeight = ref(document.documentElement.scrollHeight - 245 + "px");
const tables = ref([]); const tables = ref<Array<any>>([]);
const columns = ref([]); const columns = ref<Array<any>>([]);
const dictOptions = ref([]); const dictOptions = ref<Array<any>>([]);
const info = ref({}); const info = ref<GenTableSchema>({} as GenTableSchema);
/**
* 将对象转换为GenInfo类型
*/
function convertToGenInfo(tableSchema: any): any {
if (!tableSchema) {
return {};
}
return {
tplCategory: tableSchema.tpl_category,
tplWebType: tableSchema.tpl_web_type,
packageName: tableSchema.package_name,
moduleName: tableSchema.module_name,
businessName: tableSchema.business_name,
functionName: tableSchema.function_name,
genType: tableSchema.gen_type,
parentMenuId: tableSchema.parent_menu_id,
genPath: tableSchema.gen_path,
subTableName: tableSchema.sub_table_name,
subTableFkName: tableSchema.sub_table_fk_name,
treeCode: tableSchema.tree_code,
treeParentCode: tableSchema.tree_parent_code,
treeName: tableSchema.tree_name
};
}
/** 提交按钮 */ /** 提交按钮 */
function submitForm() { function submitForm() {
const basicForm = proxy.$refs.basicInfo.$refs.basicInfoForm; // 简化表单验证逻辑
const genForm = proxy.$refs.genInfo.$refs.genInfoForm;
Promise.all([basicForm, genForm].map(getFormPromise)).then(res => {
const validateResult = res.every(item => !!item);
if (validateResult) {
const genTable = Object.assign({}, info.value); const genTable = Object.assign({}, info.value);
genTable.columns = columns.value; genTable.columns = columns.value;
genTable.params = { genTable.tree_code = info.value.tree_code;
treeCode: info.value.treeCode, genTable.tree_name = info.value.tree_name;
treeName: info.value.treeName, genTable.tree_parent_code = info.value.tree_parent_code;
treeParentCode: info.value.treeParentCode, genTable.parent_menu_id = info.value.parent_menu_id;
parentMenuId: info.value.parentMenuId
}; // 确保id存在且为number类型
GencodeAPI.updateGenTable(genTable).then(res => { if (info.value && info.value.id !== undefined) {
proxy.$modal.msgSuccess(res.msg); GencodeAPI.updateGenTable(Number(info.value.id), genTable).then((res: any) => {
if (res.code === 200) { ElMessage.success(res.data.message || "更新成功");
if (res.data.code === 200) {
close(); close();
} }
}); });
} else { } else {
proxy.$modal.msgError("表单校验未通过,请重新检查提交内容"); ElMessage.error("表ID不存在,无法更新");
} }
});
}
function getFormPromise(form) {
return new Promise(resolve => {
form.validate(res => {
resolve(res);
});
});
} }
function close() { function close() {
const obj = { path: "/tool/gen", query: { t: Date.now(), pageNum: route.query.pageNum } }; const pageNum = route.query.page_no || route.query.pageNum;
proxy.$tab.closeOpenPage(obj); router.push({ path: "/tool/gen", query: { t: Date.now(), page_no: pageNum } });
} }
(() => { (() => {
const tableId = route.params && route.params.tableId; const tableId = route.params && route.params.tableId;
if (tableId) { if (tableId) {
// 获取表详细信息 // 获取表详细信息
GencodeAPI.getGenTable(tableId).then(res => { GencodeAPI.getGenTableDetail(Number(tableId)).then(res => {
columns.value = res.data.rows; if (res.data && res.data.data) {
info.value = res.data.info; columns.value = res.data.data.rows || [];
tables.value = res.data.tables; // 确保info包含所有必要的字段,特别是columns
const tableInfo = res.data.data.info || {};
info.value = {
...tableInfo,
columns: columns.value
};
tables.value = res.data.data.tables || [];
}
}); });
/** 查询字典下拉列表 */ /** 查询字典下拉列表 */
DictAPI.getDictTypeOptionselect.then(response => { DictAPI.getDictTypeOptionselect().then((response: any) => {
dictOptions.value = response.data; dictOptions.value = response.data.data || [];
}); });
} }
})(); })();
@@ -119,7 +119,7 @@
</el-button> </el-button>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item @click="info.genPath = '/'">恢复默认的生成基础路径</el-dropdown-item> <el-dropdown-item @click="handleResetGenPath">恢复默认的生成基础路径</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
@@ -142,7 +142,7 @@
</template> </template>
<el-select v-model="info.treeCode" placeholder="请选择"> <el-select v-model="info.treeCode" placeholder="请选择">
<el-option <el-option
v-for="(column, index) in info.columns" v-for="(column, index) in info.columns || []"
:key="index" :key="index"
:label="column.columnName + '' + column.columnComment" :label="column.columnName + '' + column.columnComment"
:value="column.columnName" :value="column.columnName"
@@ -160,7 +160,7 @@
</template> </template>
<el-select v-model="info.treeParentCode" placeholder="请选择"> <el-select v-model="info.treeParentCode" placeholder="请选择">
<el-option <el-option
v-for="(column, index) in info.columns" v-for="(column, index) in info.columns || []"
:key="index" :key="index"
:label="column.columnName + '' + column.columnComment" :label="column.columnName + '' + column.columnComment"
:value="column.columnName" :value="column.columnName"
@@ -178,7 +178,7 @@
</template> </template>
<el-select v-model="info.treeName" placeholder="请选择"> <el-select v-model="info.treeName" placeholder="请选择">
<el-option <el-option
v-for="(column, index) in info.columns" v-for="(column, index) in info.columns || []"
:key="index" :key="index"
:label="column.columnName + '' + column.columnComment" :label="column.columnName + '' + column.columnComment"
:value="column.columnName" :value="column.columnName"
@@ -202,10 +202,11 @@
</template> </template>
<el-select v-model="info.subTableName" placeholder="请选择" @change="subSelectChange"> <el-select v-model="info.subTableName" placeholder="请选择" @change="subSelectChange">
<el-option <el-option
v-for="(table, index) in tables" v-for="(table, index) in tables || []"
:key="index" :key="index"
:label="table.tableName + '' + table.tableComment" :label="(table.tableName || '') + '' + (table.tableComment || '')"
:value="table.tableName" :value="table.tableName || ''"
:disabled="!table.tableName"
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -234,21 +235,78 @@
</el-form> </el-form>
</template> </template>
<script setup> <script setup lang="ts">
import MenuAPI from "@/api/system/menu"; import MenuAPI from "@/api/system/menu";
import { ref, computed, onMounted, watch } from 'vue';
import { useRouter } from 'vue-router';
const subColumns = ref([]); const subColumns = ref<Array<{ columnName: string; columnComment: string }>>([]);
const menuOptions = ref([]); const menuOptions = ref<Array<{ id: number; menuId: number; menuName: string; parent_id: number; children: Array<any> }>>([]);
const { proxy } = getCurrentInstance(); const router = useRouter();
const props = defineProps({ // 定义类型接口
info: { interface TableColumn {
type: Object, columnName: string;
default: null columnComment: string;
}
interface TableInfo {
tableName?: string;
tableComment?: string;
columns?: TableColumn[];
}
interface GenInfo {
tplCategory?: string;
tplWebType?: string;
packageName?: string;
moduleName?: string;
businessName?: string;
functionName?: string;
genType?: string;
parentMenuId?: number;
genPath?: string;
subTableName?: string;
subTableFkName?: string;
columns?: TableColumn[];
treeCode?: string;
treeParentCode?: string;
treeName?: string;
}
const props = defineProps<{
info?: GenInfo;
tables?: TableInfo[];
}>();
const emit = defineEmits<{
(e: 'update:info', value: GenInfo): void;
}>();
// 使用computed创建一个安全的info对象,确保所有属性都有默认值
const info = computed<GenInfo>({
get() {
return {
tplCategory: '',
tplWebType: 'element-plus',
packageName: '',
moduleName: '',
businessName: '',
functionName: '',
genType: '0',
parentMenuId: undefined,
genPath: '',
subTableName: '',
subTableFkName: '',
columns: [],
treeCode: '',
treeParentCode: '',
treeName: '',
...props.info
};
}, },
tables: { set(newValue: GenInfo) {
type: Array, emit('update:info', newValue);
default: null
} }
}); });
@@ -262,44 +320,104 @@ const rules = ref({
}); });
function subSelectChange() { function subSelectChange() {
props.info.subTableFkName = ""; emit('update:info', {
...info.value,
subTableFkName: ""
});
} }
function tplSelectChange(value) { function tplSelectChange(value: string) {
if (value !== "sub") { if (value !== "sub") {
props.info.subTableName = ""; emit('update:info', {
props.info.subTableFkName = ""; ...info.value,
subTableName: "",
subTableFkName: ""
});
} }
} }
function setSubTableColumns(value) { function setSubTableColumns(value?: string) {
for (const item in props.tables) { if (!value || !props.tables) {
const name = props.tables[item].tableName; subColumns.value = [];
if (value === name) { return;
subColumns.value = props.tables[item].columns; }
for (const item of props.tables) {
if (item.tableName === value && item.columns) {
subColumns.value = item.columns;
break; break;
} }
} }
} }
// 恢复默认生成路径的方法
function handleResetGenPath() {
emit('update:info', {
...info.value,
genPath: '/'
});
}
/** 查询菜单下拉树结构 */ /** 查询菜单下拉树结构 */
function getMenuTreeselect() { function getMenuTreeselect() {
MenuAPI.getMenuList().then(response => { MenuAPI.getMenuList().then((response: any) => {
menuOptions.value = proxy.handleTree(response.data, "menuId"); // 简单的树形结构处理逻辑
function buildTree(data: any[], idField: string): any[] {
const result: any[] = [];
const map: Record<string, any> = {};
// 构建id映射
data.forEach(item => {
map[item[idField]] = item;
item.children = [];
// 转换属性名以匹配tree-select的期望格式
if (item.id !== undefined) {
item.menuId = item.id;
}
if (item.menu_name !== undefined) {
item.menuName = item.menu_name;
}
});
// 构建树
data.forEach(item => {
if (item.parent_id === 0 || !map[item.parent_id]) {
result.push(item);
} else {
map[item.parent_id].children.push(item);
}
});
return result;
}
if (response && response.data && response.data.data) {
menuOptions.value = buildTree(response.data.data, "id");
}
}); });
} }
onMounted(() => { onMounted(() => {
getMenuTreeselect(); getMenuTreeselect();
}) // 初始化时检查tplWebType是否为空
if (!props.info?.tplWebType) {
emit('update:info', {
...info.value,
tplWebType: "element-plus"
});
}
});
watch(() => props.info.subTableName, val => { watch(() => props.info?.subTableName, (val) => {
setSubTableColumns(val); setSubTableColumns(val);
}); });
watch(() => props.info.tplWebType, val => { watch(() => props.info?.tplWebType, (val) => {
if (val === '') { if (val === '' || val === undefined) {
props.info.tplWebType = "element-plus"; emit('update:info', {
...info.value,
tplWebType: "element-plus"
});
} }
}); });
@@ -57,14 +57,16 @@
</el-dialog> </el-dialog>
</template> </template>
<script setup> <script setup lang="ts">
import GencodeAPI from "@/api/generator/gencode"; import GencodeAPI from "@/api/generator/gencode";
import { ElMessage } from 'element-plus';
const total = ref(0); const total = ref(0);
const visible = ref(false); const visible = ref(false);
const tables = ref([]); const tables = ref<Array<string>>([]);
const dbTableList = ref([]); const dbTableList = ref<Array<any>>([]);
const { proxy } = getCurrentInstance(); const queryRef = ref();
const table = ref();
const queryFormData = reactive({ const queryFormData = reactive({
page_no: 1, page_no: 1,
@@ -82,12 +84,12 @@ function show() {
} }
/** 单击选择行 */ /** 单击选择行 */
function clickRow(row) { function clickRow(row: any) {
proxy.$refs.table.toggleRowSelection(row); table.value?.toggleRowSelection(row);
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
function handleSelectionChange(selection) { function handleSelectionChange(selection: Array<any>) {
tables.value = selection.map(item => item.table_name); tables.value = selection.map(item => item.table_name);
} }
@@ -108,7 +110,9 @@ function handleQuery() {
/** 重置按钮操作 */ /** 重置按钮操作 */
function resetQuery() { function resetQuery() {
proxy.resetForm("queryRef"); if (queryRef.value) {
queryRef.value.resetFields();
}
handleQuery(); handleQuery();
} }
@@ -116,12 +120,13 @@ function resetQuery() {
function handleImportTable() { function handleImportTable() {
const tableNames = tables.value.join(","); const tableNames = tables.value.join(",");
if (tableNames == "") { if (tableNames == "") {
proxy.$modal.msgError("请选择要导入的表"); ElMessage.error("请选择要导入的表");
return; return;
} }
GencodeAPI.importTable({ tables: tableNames }).then(res => { // 因为tables.value已经是string[]类型了,直接传入
proxy.$modal.msgSuccess(res.msg); GencodeAPI.importTable(tables.value).then((res: any) => {
if (res.code === 200) { ElMessage.success(res.data.message);
if (res.data.code === 200) {
visible.value = false; visible.value = false;
emit("ok"); emit("ok");
} }
+46 -45
View File
@@ -209,11 +209,11 @@
<el-tabs v-model="preview.activeName"> <el-tabs v-model="preview.activeName">
<el-tab-pane <el-tab-pane
v-for="(value, key) in preview.data" v-for="(value, key) in preview.data"
:label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.jinja2'))" :label="String(key).substring(String(key).lastIndexOf('/')+1,String(key).indexOf('.jinja2'))"
:name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.jinja2'))" :name="String(key).substring(String(key).lastIndexOf('/')+1,String(key).indexOf('.jinja2'))"
:key="value" :key="value"
> >
<el-link :underline="false" icon="DocumentCopy" @click="() => navigator.clipboard.writeText(value).then(() => copyTextSuccess())" style="float:right">&nbsp;复制</el-link> <el-link :underline="false" icon="DocumentCopy" @click="() => handleCopyText(value)" style="float:right">&nbsp;复制</el-link>
<pre>{{ value }}</pre> <pre>{{ value }}</pre>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@@ -240,14 +240,14 @@ const route = useRoute();
const importRef = ref(); const importRef = ref();
const createRef = ref(); const createRef = ref();
const tableList = ref([]); const tableList = ref<Array<any>>([]);
const loading = ref(true); const loading = ref(true);
const ids = ref([]); const ids = ref<Array<number>>([]);
const single = ref(true); const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const tableNames = ref([]); const tableNames = ref<Array<string>>([]);
const dateRange = ref([]); const dateRange = ref<Array<string>>([]);
const uniqueId = ref(""); const uniqueId = ref("");
@@ -256,9 +256,7 @@ const data = reactive({
page_no: 1, page_no: 1,
page_size: 10, page_size: 10,
table_name: undefined, table_name: undefined,
table_comment: undefined, table_comment: undefined
start_time: undefined,
end_time: undefined
}, },
preview: { preview: {
open: false, open: false,
@@ -286,8 +284,8 @@ const { queryFormData, preview } = toRefs(data);
onActivated(() => { onActivated(() => {
const time = route.query.t; const time = route.query.t;
if (time != null && time != uniqueId.value) { if (time != null && String(time) != uniqueId.value) {
uniqueId.value = time; uniqueId.value = String(time);
queryFormData.value.page_no = Number(route.query.page_no || 1); queryFormData.value.page_no = Number(route.query.page_no || 1);
dateRange.value = []; dateRange.value = [];
loadingData(); loadingData();
@@ -302,12 +300,6 @@ function loadingData() {
...queryFormData.value ...queryFormData.value
}; };
// 如果有日期范围,添加到查询参数中
if (dateRange.value && dateRange.value.length === 2) {
queryParams.start_time = dateRange.value[0];
queryParams.end_time = dateRange.value[1];
}
GencodeAPI.listTable(queryParams).then(response => { GencodeAPI.listTable(queryParams).then(response => {
tableList.value = response.data.data.items; tableList.value = response.data.data.items;
total.value = response.data.data.total; total.value = response.data.data.total;
@@ -322,32 +314,32 @@ function handleQuery() {
} }
/** 生成代码操作 */ /** 生成代码操作 */
function handleGenTable(row) { function handleGenTable(row: any) {
const tbNames = row?.table_name || tableNames.value; const tbNames = row?.table_name || tableNames.value;
if (!tbNames || (Array.isArray(tbNames) && tbNames.length === 0)) { if (!tbNames || (Array.isArray(tbNames) && tbNames.length === 0)) {
ElMessage.error("请选择要生成的数据"); ElMessage.error("请选择要生成的数据");
return; return;
} }
if (row?.genType === "1") { if (row?.gen_type === "1") {
GencodeAPI.genCodeToPath(row.tableName).then(() => { GencodeAPI.genCodeToPath(row.table_name).then(() => {
ElMessage.success("成功生成到自定义路径:" + row.genPath); ElMessage.success("成功生成到自定义路径:" + row.gen_path);
}); });
} else { } else {
GencodeAPI.batchGenCode(tbNames).then(response => { GencodeAPI.batchGenCode(tbNames).then((response: any) => {
const blob = new Blob([response], { type: 'application/zip' }); const blob = new Blob([response.data], { type: 'application/zip' });
const url = window.URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const link = document.createElement('a'); const link = document.createElement('a');
link.href = url; link.href = url;
link.download = 'code.zip'; link.download = 'code.zip';
link.click(); link.click();
window.URL.revokeObjectURL(url); URL.revokeObjectURL(url);
}); });
} }
} }
/** 同步数据库操作 */ /** 同步数据库操作 */
function handleSynchDb(row) { function handleSynchDb(row: any) {
const tableName = row.table_name; const tableName = row.table_name;
ElMessageBox.confirm( ElMessageBox.confirm(
'确认要强制同步"' + tableName + '"表结构吗?', '确认要强制同步"' + tableName + '"表结构吗?',
@@ -386,16 +378,14 @@ function handleRefresh() {
page_no: 1, page_no: 1,
page_size: 10, page_size: 10,
table_name: undefined, table_name: undefined,
table_comment: undefined, table_comment: undefined
start_time: undefined,
end_time: undefined
}; };
handleQuery(); handleQuery();
} }
/** 预览按钮 */ /** 预览按钮 */
function handlePreview(row) { function handlePreview(row: any) {
GencodeAPI.previewTable(row.tableId).then(response => { GencodeAPI.previewTable(row.id).then(response => {
preview.value.data = response.data; preview.value.data = response.data;
preview.value.open = true; preview.value.open = true;
preview.value.activeName = "do.py"; preview.value.activeName = "do.py";
@@ -407,23 +397,34 @@ function copyTextSuccess() {
ElMessage.success("复制成功"); ElMessage.success("复制成功");
} }
/** 处理文本复制 */
function handleCopyText(value: string) {
if (window && window.navigator && window.navigator.clipboard) {
window.navigator.clipboard.writeText(value).then(() => {
copyTextSuccess();
}).catch(() => {
ElMessage.error("复制失败");
});
}
}
// 多选框选中数据 // 多选框选中数据
function handleSelectionChange(selection) { function handleSelectionChange(selection: Array<any>) {
ids.value = selection.map(item => item.tableId); ids.value = selection.map((item: any) => item.id);
tableNames.value = selection.map(item => item.table_name); tableNames.value = selection.map((item: any) => item.table_name);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
function handleEditTable(row) { function handleEditTable(row: any) {
const tableId = row.tableId || ids.value[0]; const tableId = row.id || ids.value[0];
router.push({ path: "/tool/gen-edit/index/" + tableId, query: { page_no: queryFormData.value.page_no } }); router.push({ path: "/tool/gen-edit/index/" + tableId, query: { page_no: queryFormData.value.page_no } });
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
function handleDelete(row) { function handleDelete(row: any) {
const tableIds = row?.tableId ? [row.tableId] : ids.value; const tableIds = row?.id ? [row.id] : ids.value;
ElMessageBox.confirm( ElMessageBox.confirm(
'是否确认删除表编号为"' + tableIds + '"的数据项?', '是否确认删除表编号为"' + tableIds + '"的数据项?',
'删除确认', '删除确认',
@@ -433,12 +434,12 @@ function handleDelete(row) {
type: 'warning' type: 'warning'
} }
).then(() => { ).then(() => {
return GencodeAPI.deleteTable(tableIds); return GencodeAPI.deleteTable({ table_ids: tableIds });
}).then(() => { }).then(() => {
loadingData(); loadingData();
ElMessage.success("删除成功"); ElMessage.success("删除成功");
}).catch(() => {}); }).catch(() => {});
} }
loadingData(); loadingData();
</script> </script>