代码生成器 下载功能

This commit is contained in:
cuijiawang
2025-09-26 11:33:14 +08:00
parent 944ecfa3bf
commit bddd3065d4
6 changed files with 618 additions and 44 deletions

View File

@@ -36,20 +36,20 @@
"@vueuse/motion": "^2.0.0",
"animate.css": "^4.1.1",
"axios": "^1.4.0",
"cropperjs": "^1.5.13",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.8",
"echarts": "^5.4.2",
"element-plus": "2.3.6",
"js-cookie": "^3.0.5",
"jsencrypt": "^3.3.2",
"jszip": "^3.10.1",
"mitt": "^3.0.0",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"path": "^0.12.7",
"pinia": "^2.1.4",
"pinyin-pro": "^3.15.2",
"cropperjs": "^1.5.13",
"vue-tippy": "^6.2.0",
"qrcode": "^1.5.3",
"qs": "^6.11.2",
"responsive-storage": "^2.2.0",
@@ -57,6 +57,7 @@
"typeit": "^8.7.1",
"vue": "^3.3.4",
"vue-router": "^4.2.2",
"vue-tippy": "^6.2.0",
"vue-types": "^5.1.0",
"xlsx": "^0.18.5"
},

View File

@@ -32,13 +32,15 @@ const updateValue = (value: string) => {
const updateLineNumbers = (content: string) => {
const lines = content.split("\n");
lineNumbers.value = lines.map((_, index) => String(index + 1).padStart(3, " "));
lineNumbers.value = lines.map((_, index) =>
String(index + 1).padStart(3, " ")
);
};
// 监听内容变化,更新行号
watch(
() => props.modelValue,
(newValue) => {
newValue => {
updateLineNumbers(newValue);
},
{ immediate: true }
@@ -47,7 +49,9 @@ watch(
// 同步滚动
const handleScroll = (event: Event) => {
const textarea = event.target as HTMLTextAreaElement;
const lineNumbersEl = textarea.parentElement?.querySelector(".line-numbers") as HTMLElement;
const lineNumbersEl = textarea.parentElement?.querySelector(
".line-numbers"
) as HTMLElement;
if (lineNumbersEl) {
lineNumbersEl.scrollTop = textarea.scrollTop;
}
@@ -74,12 +78,12 @@ const handleKeydown = (event: KeyboardEvent) => {
// 计算动态高度
const dynamicHeight = computed(() => {
if (props.readonly && props.modelValue) {
const lines = props.modelValue.split('\n').length;
const lines = props.modelValue.split("\n").length;
const lineHeight = 19.5; // 匹配CSS中的line-height
const padding = 16; // top + bottom padding
const minHeight = 200;
const calculatedHeight = lines * lineHeight + padding;
return Math.max(calculatedHeight, minHeight) + 'px';
return Math.max(calculatedHeight, minHeight) + "px";
}
return props.height;
});
@@ -135,7 +139,7 @@ const dynamicHeight = computed(() => {
background: var(--el-bg-color-page);
border-right: 1px solid var(--el-border-color-lighter);
padding: 8px 4px;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-family: "Consolas", "Monaco", "Courier New", monospace;
font-size: 13px;
line-height: 1.5;
color: var(--el-text-color-secondary);
@@ -167,7 +171,7 @@ const dynamicHeight = computed(() => {
outline: none;
resize: none;
padding: 8px 12px;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-family: "Consolas", "Monaco", "Courier New", monospace;
font-size: 13px;
line-height: 1.5;
background: transparent;

View File

@@ -25,7 +25,12 @@ const updateValue = (key: keyof CodegenOptions, value: any) => {
// 配置分组
const basicConfig = [
{ key: "dataType", label: "解析引擎", type: "select", options: "dataTypeOptions" },
{
key: "dataType",
label: "解析引擎",
type: "select",
options: "dataTypeOptions"
},
{ key: "authorName", label: "作者", type: "input" },
{ key: "packageName", label: "包名", type: "input" }
];
@@ -37,9 +42,24 @@ const returnConfig = [
];
const typeConfig = [
{ key: "tinyintTransType", label: "TinyInt转换", type: "select", options: "tinyintTransTypeOptions" },
{ key: "timeTransType", label: "时间类型", type: "select", options: "timeTransTypeOptions" },
{ key: "nameCaseType", label: "命名类型", type: "select", options: "nameCaseTypeOptions" }
{
key: "tinyintTransType",
label: "TinyInt转换",
type: "select",
options: "tinyintTransTypeOptions"
},
{
key: "timeTransType",
label: "时间类型",
type: "select",
options: "timeTransTypeOptions"
},
{
key: "nameCaseType",
label: "命名类型",
type: "select",
options: "nameCaseTypeOptions"
}
];
const switchConfig = [
@@ -67,7 +87,7 @@ const switchConfig = [
v-if="config.type === 'select'"
:model-value="modelValue[config.key]"
class="w-full"
@update:model-value="(val) => updateValue(config.key, val)"
@update:model-value="val => updateValue(config.key, val)"
>
<el-option
v-for="option in props[config.options]"
@@ -79,7 +99,7 @@ const switchConfig = [
<el-input
v-else
:model-value="modelValue[config.key]"
@update:model-value="(val) => updateValue(config.key, val)"
@update:model-value="val => updateValue(config.key, val)"
/>
</el-form-item>
</div>
@@ -96,7 +116,7 @@ const switchConfig = [
>
<el-input
:model-value="modelValue[config.key]"
@update:model-value="(val) => updateValue(config.key, val)"
@update:model-value="val => updateValue(config.key, val)"
/>
</el-form-item>
</div>
@@ -114,7 +134,7 @@ const switchConfig = [
<el-select
:model-value="modelValue[config.key]"
class="w-full"
@update:model-value="(val) => updateValue(config.key, val)"
@update:model-value="val => updateValue(config.key, val)"
>
<el-option
v-for="option in props[config.options]"
@@ -139,7 +159,7 @@ const switchConfig = [
>
<el-switch
:model-value="modelValue[config.key]"
@update:model-value="(val) => updateValue(config.key, val)"
@update:model-value="val => updateValue(config.key, val)"
/>
</el-form-item>
</div>
@@ -165,14 +185,18 @@ const switchConfig = [
padding-left: 12px;
&::before {
content: '';
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 16px;
background: linear-gradient(135deg, var(--el-color-primary) 0%, var(--el-color-primary-light-3) 100%);
background: linear-gradient(
135deg,
var(--el-color-primary) 0%,
var(--el-color-primary-light-3) 100%
);
border-radius: 2px;
}
}
@@ -203,7 +227,8 @@ const switchConfig = [
font-size: 14px;
}
.el-input, .el-select {
.el-input,
.el-select {
.el-input__wrapper {
border-radius: 8px;
transition: all 0.2s ease;
@@ -234,7 +259,11 @@ const switchConfig = [
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: linear-gradient(135deg, var(--el-bg-color) 0%, var(--el-bg-color-page) 100%);
background: linear-gradient(
135deg,
var(--el-bg-color) 0%,
var(--el-bg-color-page) 100%
);
border-radius: 8px;
border: 1px solid var(--el-border-color-lighter);
transition: all 0.3s ease;
@@ -242,13 +271,17 @@ const switchConfig = [
overflow: hidden;
&::before {
content: '';
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--el-color-primary-light-8) 0%, var(--el-color-primary-light-6) 100%);
background: linear-gradient(
90deg,
var(--el-color-primary-light-8) 0%,
var(--el-color-primary-light-6) 100%
);
transform: scaleX(0);
transform-origin: left;
transition: transform 0.3s ease;

View File

@@ -8,6 +8,8 @@ import CodeEditor from "./components/CodeEditor.vue";
import Play from "@iconify-icons/ep/caret-right";
import CopyDocument from "@iconify-icons/ep/document-copy";
import Download from "@iconify-icons/ep/download";
import FolderOpened from "@iconify-icons/ep/folder-opened";
import ArrowDown from "@iconify-icons/ep/arrow-down";
import ArrowUp from "@iconify-icons/ep/arrow-up";
@@ -33,6 +35,8 @@ const {
setOutputModel,
generateCode,
copyCode,
downloadCurrentCode,
downloadAllCode,
switchHistoricalData
} = useCodegen();
@@ -52,8 +56,12 @@ onMounted(() => {
<div class="p-4">
<!-- 页面标题 -->
<div class="mb-6">
<h1 class="text-2xl font-bold text-gray-800 dark:text-white flex items-center">
<el-icon class="mr-2"><component :is="useRenderIcon('ep:code')" /></el-icon>
<h1
class="text-2xl font-bold text-gray-800 dark:text-white flex items-center"
>
<el-icon class="mr-2"
><component :is="useRenderIcon('ep:code')"
/></el-icon>
代码生成器
</h1>
<p class="text-gray-600 dark:text-gray-300 mt-2">
@@ -64,10 +72,16 @@ onMounted(() => {
<!-- 历史记录区域 -->
<el-card v-if="hasHistory" class="mb-4 collapsible-card" shadow="hover">
<template #header>
<div class="collapsible-header" @click="historyCollapsed = !historyCollapsed">
<div
class="collapsible-header"
@click="historyCollapsed = !historyCollapsed"
>
<h3 class="text-lg font-semibold">
历史记录
<el-icon class="collapse-arrow" :class="{ 'collapsed': historyCollapsed }">
<el-icon
class="collapse-arrow"
:class="{ collapsed: historyCollapsed }"
>
<component :is="useRenderIcon(ArrowDown)" />
</el-icon>
</h3>
@@ -97,7 +111,10 @@ onMounted(() => {
<div class="collapsible-header" @click="sqlCollapsed = !sqlCollapsed">
<h3 class="text-lg font-semibold">
输入SQL
<el-icon class="collapse-arrow" :class="{ 'collapsed': sqlCollapsed }">
<el-icon
class="collapse-arrow"
:class="{ collapsed: sqlCollapsed }"
>
<component :is="useRenderIcon(ArrowDown)" />
</el-icon>
</h3>
@@ -120,10 +137,16 @@ onMounted(() => {
<el-card class="mb-4 collapsible-card" shadow="hover">
<template #header>
<div class="flex justify-between items-center">
<div class="collapsible-header" @click="configCollapsed = !configCollapsed">
<div
class="collapsible-header"
@click="configCollapsed = !configCollapsed"
>
<h3 class="text-lg font-semibold">
生成设置
<el-icon class="collapse-arrow" :class="{ 'collapsed': configCollapsed }">
<el-icon
class="collapse-arrow"
:class="{ collapsed: configCollapsed }"
>
<component :is="useRenderIcon(ArrowDown)" />
</el-icon>
</h3>
@@ -155,10 +178,16 @@ onMounted(() => {
<!-- 模板选择区域 -->
<el-card class="mb-4 collapsible-card" shadow="hover">
<template #header>
<div class="collapsible-header" @click="templateCollapsed = !templateCollapsed">
<div
class="collapsible-header"
@click="templateCollapsed = !templateCollapsed"
>
<h3 class="text-lg font-semibold">
模板选择
<el-icon class="collapse-arrow" :class="{ 'collapsed': templateCollapsed }">
<el-icon
class="collapse-arrow"
:class="{ collapsed: templateCollapsed }"
>
<component :is="useRenderIcon(ArrowDown)" />
</el-icon>
</h3>
@@ -182,14 +211,32 @@ onMounted(() => {
<template #header>
<div class="flex justify-between items-center">
<h3 class="text-lg font-semibold">输出代码</h3>
<el-button
type="primary"
:icon="useRenderIcon(CopyDocument)"
:disabled="!hasOutput"
@click="copyCode"
>
复制代码
</el-button>
<div class="flex gap-2">
<el-button
type="success"
:icon="useRenderIcon(Download)"
:disabled="!hasOutput"
@click="downloadCurrentCode"
>
下载当前文件
</el-button>
<el-button
type="warning"
:icon="useRenderIcon(FolderOpened)"
:disabled="!hasOutput"
@click="downloadAllCode"
>
下载所有文件
</el-button>
<el-button
type="primary"
:icon="useRenderIcon(CopyDocument)"
:disabled="!hasOutput"
@click="copyCode"
>
复制代码
</el-button>
</div>
</div>
</template>
@@ -221,7 +268,11 @@ onMounted(() => {
}
.el-card__header {
background: linear-gradient(135deg, var(--el-color-primary-light-9) 0%, var(--el-bg-color) 100%);
background: linear-gradient(
135deg,
var(--el-color-primary-light-9) 0%,
var(--el-bg-color) 100%
);
border-bottom: 1px solid var(--el-border-color-lighter);
h3 {
@@ -321,11 +372,19 @@ onMounted(() => {
}
&.el-button--primary {
background: linear-gradient(135deg, var(--el-color-primary) 0%, var(--el-color-primary-dark-2) 100%);
background: linear-gradient(
135deg,
var(--el-color-primary) 0%,
var(--el-color-primary-dark-2) 100%
);
border: none;
&:hover {
background: linear-gradient(135deg, var(--el-color-primary-light-2) 0%, var(--el-color-primary) 100%);
background: linear-gradient(
135deg,
var(--el-color-primary-light-2) 0%,
var(--el-color-primary) 100%
);
}
}
}

View File

@@ -0,0 +1,435 @@
/**
* 代码下载工具函数
* 支持根据代码内容自动识别文件类型和生成文件名
*/
import JSZip from 'jszip';
// 文件类型映射
const FILE_TYPE_PATTERNS = {
java: [
/public\s+class\s+\w+/,
/public\s+interface\s+\w+/,
/public\s+enum\s+\w+/,
/@Entity/,
/@Service/,
/@Controller/,
/@Repository/,
/@Component/,
/package\s+[\w.]+;/,
/import\s+[\w.]+;/
],
xml: [
/<\?xml/,
/<mapper/,
/<select/,
/<insert/,
/<update/,
/<delete/,
/<configuration>/,
/<beans>/
],
sql: [
/CREATE\s+TABLE/i,
/ALTER\s+TABLE/i,
/INSERT\s+INTO/i,
/SELECT\s+.*\s+FROM/i,
/UPDATE\s+.*\s+SET/i,
/DELETE\s+FROM/i,
/DROP\s+TABLE/i
],
js: [
/function\s+\w+/,
/const\s+\w+\s*=/,
/let\s+\w+\s*=/,
/var\s+\w+\s*=/,
/export\s+/,
/import\s+.*\s+from/,
/module\.exports/,
/require\(/
],
ts: [
/interface\s+\w+/,
/type\s+\w+/,
/export\s+interface/,
/export\s+type/,
/:\s*\w+(\[\])?/,
/import\s+.*\s+from\s+['"].*\.ts['"]/
],
vue: [
/<template>/,
/<script.*>/,
/<style.*>/,
/export\s+default\s*{/,
/defineComponent/
],
json: [
/^\s*{/,
/^\s*\[/,
/"[\w-]+"\s*:/
],
yml: [
/^[\w-]+:/,
/^\s+-\s/,
/^---/
],
properties: [
/^[\w.-]+\s*=/,
/^#/,
/spring\./,
/server\./
],
html: [
/<!DOCTYPE\s+html>/i,
/<html/i,
/<head>/i,
/<body>/i,
/<div/i,
/<p>/i
],
css: [
/\.[a-zA-Z-]+\s*{/,
/#[a-zA-Z-]+\s*{/,
/@media/,
/@import/,
/:\s*[\w#.-]+;/
],
scss: [
/\$[\w-]+:/,
/@mixin/,
/@include/,
/@extend/,
/&:/
]
};
/**
* 根据代码内容识别文件类型
* @param content 代码内容
* @returns 文件扩展名
*/
export function detectFileType(content: string): string {
if (!content || content.trim().length === 0) {
return 'txt';
}
const trimmedContent = content.trim();
// 遍历所有文件类型模式
for (const [fileType, patterns] of Object.entries(FILE_TYPE_PATTERNS)) {
for (const pattern of patterns) {
if (pattern.test(trimmedContent)) {
return fileType;
}
}
}
// 默认返回txt
return 'txt';
}
/**
* 从代码内容中提取有意义的文件名
* @param content 代码内容
* @param fileType 文件类型
* @returns 文件名(不含扩展名)
*/
export function extractFileName(content: string, fileType: string): string {
if (!content || content.trim().length === 0) {
return 'untitled';
}
const trimmedContent = content.trim();
switch (fileType) {
case 'java':
return extractJavaFileName(trimmedContent);
case 'xml':
return extractXmlFileName(trimmedContent);
case 'sql':
return extractSqlFileName(trimmedContent);
case 'js':
case 'ts':
return extractJsFileName(trimmedContent);
case 'vue':
return extractVueFileName(trimmedContent);
default:
return generateGenericFileName(trimmedContent);
}
}
/**
* 提取Java文件名
*/
function extractJavaFileName(content: string): string {
// 优先提取类名
const classMatch = content.match(/public\s+class\s+(\w+)/);
if (classMatch) {
return classMatch[1];
}
// 提取接口名
const interfaceMatch = content.match(/public\s+interface\s+(\w+)/);
if (interfaceMatch) {
return interfaceMatch[1];
}
// 提取枚举名
const enumMatch = content.match(/public\s+enum\s+(\w+)/);
if (enumMatch) {
return enumMatch[1];
}
// 从注解中推断
if (content.includes('@Entity')) {
const entityMatch = content.match(/@Table\s*\(\s*name\s*=\s*["'](\w+)["']/);
if (entityMatch) {
return toCamelCase(entityMatch[1]) + 'Entity';
}
}
if (content.includes('@Service')) {
return 'Service';
}
if (content.includes('@Controller')) {
return 'Controller';
}
if (content.includes('@Repository')) {
return 'Repository';
}
return 'JavaClass';
}
/**
* 提取XML文件名
*/
function extractXmlFileName(content: string): string {
// MyBatis Mapper
const mapperMatch = content.match(/<mapper\s+namespace\s*=\s*["']([\w.]+)["']/);
if (mapperMatch) {
const namespace = mapperMatch[1];
const parts = namespace.split('.');
return parts[parts.length - 1] + 'Mapper';
}
// Spring配置
if (content.includes('<beans')) {
return 'applicationContext';
}
// MyBatis配置
if (content.includes('<configuration>')) {
return 'mybatis-config';
}
return 'config';
}
/**
* 提取SQL文件名
*/
function extractSqlFileName(content: string): string {
// 提取表名
const createTableMatch = content.match(/CREATE\s+TABLE\s+[`'"]*(\w+)[`'"]*/i);
if (createTableMatch) {
return createTableMatch[1];
}
const alterTableMatch = content.match(/ALTER\s+TABLE\s+[`'"]*(\w+)[`'"]*/i);
if (alterTableMatch) {
return alterTableMatch[1] + '_alter';
}
const insertMatch = content.match(/INSERT\s+INTO\s+[`'"]*(\w+)[`'"]*/i);
if (insertMatch) {
return insertMatch[1] + '_data';
}
return 'database';
}
/**
* 提取JS/TS文件名
*/
function extractJsFileName(content: string): string {
// 提取导出的类名或函数名
const exportClassMatch = content.match(/export\s+class\s+(\w+)/);
if (exportClassMatch) {
return exportClassMatch[1];
}
const exportFunctionMatch = content.match(/export\s+function\s+(\w+)/);
if (exportFunctionMatch) {
return exportFunctionMatch[1];
}
const exportConstMatch = content.match(/export\s+const\s+(\w+)/);
if (exportConstMatch) {
return exportConstMatch[1];
}
// 提取接口或类型名
const interfaceMatch = content.match(/interface\s+(\w+)/);
if (interfaceMatch) {
return interfaceMatch[1];
}
const typeMatch = content.match(/type\s+(\w+)/);
if (typeMatch) {
return typeMatch[1];
}
return 'script';
}
/**
* 提取Vue文件名
*/
function extractVueFileName(content: string): string {
// 从defineOptions中提取name
const defineOptionsMatch = content.match(/defineOptions\s*\(\s*{\s*name:\s*["'](\w+)["']/);
if (defineOptionsMatch) {
return defineOptionsMatch[1];
}
// 从export default中的name属性提取
const exportNameMatch = content.match(/export\s+default\s*{\s*name:\s*["'](\w+)["']/);
if (exportNameMatch) {
return exportNameMatch[1];
}
return 'Component';
}
/**
* 生成通用文件名
*/
function generateGenericFileName(content: string): string {
// 提取第一行作为文件名(去除特殊字符)
const firstLine = content.split('\n')[0].trim();
if (firstLine.length > 0) {
const cleanName = firstLine
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '_')
.substring(0, 20);
if (cleanName.length > 0) {
return cleanName;
}
}
return 'untitled';
}
/**
* 转换为驼峰命名
*/
function toCamelCase(str: string): string {
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
.replace(/^[a-z]/, letter => letter.toUpperCase());
}
/**
* 下载单个文件
* @param content 文件内容
* @param originalFileName 原始文件名(可选)
*/
export function downloadSingleFile(content: string, originalFileName?: string): void {
if (!content || content.trim().length === 0) {
console.warn('文件内容为空,无法下载');
return;
}
let fileName = originalFileName;
if (!fileName) {
const fileType = detectFileType(content);
const baseName = extractFileName(content, fileType);
fileName = `${baseName}.${fileType}`;
}
// 创建Blob对象
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
// 创建下载链接
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
// 触发下载
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
/**
* 批量下载文件需要jszip库支持
* @param files 文件映射 { fileName: content }
* @param zipFileName zip文件名
*/
export async function downloadMultipleFiles(
files: Record<string, string>,
zipFileName: string = 'generated-code.zip'
): Promise<void> {
try {
// 创建JSZip实例
const zip = new JSZip();
// 添加文件到zip
Object.entries(files).forEach(([templateName, content]) => {
if (content && content.trim().length > 0) {
const fileType = detectFileType(content);
const baseName = extractFileName(content, fileType);
const fileName = `${baseName}.${fileType}`;
zip.file(fileName, content);
}
});
// 生成zip文件
const zipContent = await zip.generateAsync({ type: 'blob' });
// 创建下载链接
const url = URL.createObjectURL(zipContent);
const link = document.createElement('a');
link.href = url;
link.download = zipFileName;
// 触发下载
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(url);
} catch (error) {
console.error('批量下载失败:', error);
// 如果jszip不可用回退到单个文件下载
console.warn('JSZip不可用回退到单个文件下载');
Object.entries(files).forEach(([templateName, content]) => {
if (content && content.trim().length > 0) {
downloadSingleFile(content);
}
});
}
}
/**
* 获取当前时间戳作为文件名后缀
*/
export function getTimestamp(): string {
const now = new Date();
return now.getFullYear().toString() +
(now.getMonth() + 1).toString().padStart(2, '0') +
now.getDate().toString().padStart(2, '0') +
'_' +
now.getHours().toString().padStart(2, '0') +
now.getMinutes().toString().padStart(2, '0') +
now.getSeconds().toString().padStart(2, '0');
}

View File

@@ -7,6 +7,11 @@ import {
TemplateInfo,
CodegenRequest
} from "@/api/system/codegen";
import {
downloadSingleFile,
downloadMultipleFiles,
getTimestamp
} from "./download";
export function useCodegen() {
// 表单数据
@@ -148,6 +153,41 @@ export function useCodegen() {
});
}
// 下载当前代码文件
function downloadCurrentCode() {
if (!outputStr.value) {
ElMessage.warning("没有可下载的内容");
return;
}
try {
downloadSingleFile(outputStr.value.trim());
ElMessage.success("下载成功");
} catch (error) {
console.error("下载失败:", error);
ElMessage.error("下载失败");
}
}
// 批量下载所有生成的代码文件
async function downloadAllCode() {
if (!outputJson.value || Object.keys(outputJson.value).length === 0) {
ElMessage.warning("没有可下载的内容");
return;
}
try {
const timestamp = getTimestamp();
const zipFileName = `generated-code-${timestamp}.zip`;
await downloadMultipleFiles(outputJson.value, zipFileName);
ElMessage.success("批量下载成功");
} catch (error) {
console.error("批量下载失败:", error);
ElMessage.error("批量下载失败");
}
}
// 设置历史记录
function setHistoricalData(tableName: string) {
// 添加新表名(如果不存在)
@@ -215,6 +255,8 @@ export function useCodegen() {
setOutputModel,
generateCode,
copyCode,
downloadCurrentCode,
downloadAllCode,
switchHistoricalData
};
}