生成器

This commit is contained in:
Kunagisa 2025-06-10 20:13:04 +08:00
parent c09f41f40c
commit 618bf4285a

View File

@ -25,6 +25,13 @@
>
下载 .vue 文件
</button>
<button
@click="downloadApiFile"
:disabled="!generatedApiCode"
class="action-button download-api-button"
>
下载 API 文件
</button>
</div>
<div v-if="generatedCode" class="generated-code-preview">
@ -106,6 +113,8 @@ import { ref } from 'vue';
const sqlInput = ref('');
const generatedCode = ref('');
const generatedFileName = ref('');
const generatedApiCode = ref('');
const generatedApiFileName = ref('');
const parsedTableInfo = ref(null);
/**
@ -196,6 +205,64 @@ const parseSql = (sql) => {
}
};
/**
* 根据表结构生成 API 文件代码
* @param {object} tableInfo - 解析后的表结构
* @returns {string} API 文件代码字符串
*/
const generateApiFile = (tableInfo) => {
const componentName = toPascalCase(tableInfo.tableName);
const resourceName = tableInfo.tableName;
const primaryKey = tableInfo.primaryKey || 'id';
return `
import axiosInstance from './axiosConfig';
const API_BASE_URL = '/api/${resourceName}';
/**
* 获取${tableInfo.tableComment || componentName}列表
* @param {object} params - 查询参数
*/
export const get${componentName}List = (params) => {
return axiosInstance.get(API_BASE_URL, { params });
};
/**
* 获取单个${tableInfo.tableComment || componentName}详情
* @param {number|string} id - 主键ID
*/
export const get${componentName}ById = (id) => {
return axiosInstance.get(\`\${API_BASE_URL}/\${id}\`);
};
/**
* 创建新的${tableInfo.tableComment || componentName}
* @param {object} data - ${tableInfo.tableComment || componentName}数据
*/
export const create${componentName} = (data) => {
return axiosInstance.post(API_BASE_URL, data);
};
/**
* 更新${tableInfo.tableComment || componentName}
* @param {number|string} id - 主键ID
* @param {object} data - 需要更新的数据
*/
export const update${componentName} = (id, data) => {
return axiosInstance.put(\`\${API_BASE_URL}/\${id}\`, data);
};
/**
* 删除${tableInfo.tableComment || componentName}
* @param {number|string} id - 主键ID
*/
export const delete${componentName} = (id) => {
return axiosInstance.delete(\`\${API_BASE_URL}/\${id}\`);
};
`;
};
/**
* 根据表结构生成 Vue 组件代码
* @param {object} tableInfo - 解析后的表结构
@ -211,115 +278,17 @@ const generateVueComponent = (tableInfo) => {
.map(c => `
<div class="form-group">
<label for="add-${c.name}">${c.comment || c.name}:</label>
<input id="add-${c.name}" type="text" v-model="editableItem.${c.name}" placeholder="请输入${c.comment || c.name}">
<input id="add-${c.name}" type="text" v-model="editableItem.${c.name}" placeholder="请输入${c.comment || c.name}" />
</div>`).join('');
const dataFields = tableInfo.columns.map(c => ` ${c.name}: '',`).join('\n');
const scriptContent = `
import { ref, onMounted } from 'vue';
// API/api/index.js
// import { get${componentName}List, create${componentName}, update${componentName}, delete${componentName} } from '@/api';
const items = ref([]);
const loading = ref(false);
const showModal = ref(false);
const isEditing = ref(false);
const editableItem = ref({
${dataFields}
});
const editError = ref('');
onMounted(() => {
fetchItems();
});
const API_URL = '/api/${tableInfo.tableName}'; // API
const fetchItems = async () => {
loading.value = true;
try {
// const response = await get${componentName}List();
// items.value = response.data;
// --- ---
items.value = [
{ ${tableInfo.columns.map((c, i) => `${c.name}: "示例值 ${i + 1}"`).join(', ')} },
{ ${tableInfo.columns.map((c, i) => `${c.name}: "示例值 ${i + 2}"`).join(', ')} }
];
// --- ---
} catch (error) {
console.error('获取列表失败:', error);
items.value = [];
} finally {
loading.value = false;
}
};
const openModal = (item = null) => {
if (item) {
isEditing.value = true;
editableItem.value = { ...item };
} else {
isEditing.value = false;
editableItem.value = {
${dataFields}
};
}
editError.value = '';
showModal.value = true;
};
const closeModal = () => {
showModal.value = false;
};
const saveItem = async () => {
editError.value = '';
try {
if (isEditing.value) {
// await update${componentName}(editableItem.value.${tableInfo.primaryKey}, editableItem.value);
console.log('更新项目:', editableItem.value);
} else {
// await create${componentName}(editableItem.value);
console.log('创建项目:', editableItem.value);
const tableColumns = tableInfo.columns.map(c => {
if (c.type.toLowerCase().includes('datetime') || c.type.toLowerCase().includes('timestamp')) {
return ` <td>{{ formatDate(item.${c.name}) }}</td>`;
}
closeModal();
fetchItems();
alert('操作成功!');
} catch (error) {
console.error('保存失败:', error);
editError.value = error.message || '操作失败,请重试。';
}
};
const confirmDeleteItem = async (id) => {
if (window.confirm('确定要删除此项吗?')) {
try {
// await delete${componentName}(id);
console.log('删除项目, id:', id);
fetchItems();
alert('删除成功!');
} catch (error) {
console.error('删除失败:', error);
alert('删除失败: ' + (error.message || '请稍后重试'));
}
}
};
//
const formatDate = (dateString) => {
if (!dateString) return 'N/A';
try {
const date = new Date(dateString);
return isNaN(date.getTime()) ? dateString : date.toLocaleString();
} catch (e) {
return dateString;
}
};
`;
return ` <td>{{ item.${c.name} }}</td>`;
}).join('\n');
const styleContent = `
.generated-component-container {
@ -489,11 +458,9 @@ const formatDate = (dateString) => {
.error-message {
color: #dc3545;
margin-top: 15px;
}
`;
}`;
return `
<template>
return `<template>
<div class="generated-component-container">
<div class="actions-bar">
<h3>${tableTitle}</h3>
@ -519,7 +486,7 @@ ${tableInfo.columns.map(c => ` <th>${c.comment || c.name}</th>`).join
<td :colspan="${tableInfo.columns.length + 1}" style="text-align: center;">暂无数据</td>
</tr>
<tr v-for="item in items" :key="item.${tableInfo.primaryKey}">
${tableInfo.columns.map(c => ` <td>{{ item.${c.name} }}</td>`).join('\n')}
${tableColumns}
<td>
<button @click="openModal(item)" class="action-button edit">编辑</button>
<button @click="confirmDeleteItem(item.${tableInfo.primaryKey})" class="action-button delete">删除</button>
@ -547,19 +514,117 @@ ${formFields}
</template>
<script setup>
${scriptContent}
</scr` + `ipt>
import { ref, onMounted } from 'vue';
import {
get${componentName}List,
get${componentName}ById,
create${componentName},
update${componentName},
delete${componentName}
} from '@/api/${tableInfo.tableName}';
const items = ref([]);
const loading = ref(false);
const showModal = ref(false);
const isEditing = ref(false);
const editableItem = ref({
${dataFields}
});
const editError = ref('');
onMounted(() => {
fetchItems();
});
const fetchItems = async () => {
loading.value = true;
try {
const response = await get${componentName}List();
items.value = response.data;
} catch (error) {
console.error('获取列表失败:', error);
items.value = [];
} finally {
loading.value = false;
}
};
const openModal = async (item = null) => {
if (item) {
isEditing.value = true;
try {
const response = await get${componentName}ById(item.${tableInfo.primaryKey});
editableItem.value = response.data;
} catch (error) {
console.error('获取详情失败:', error);
editableItem.value = { ...item };
}
} else {
isEditing.value = false;
editableItem.value = {
${dataFields}
};
}
editError.value = '';
showModal.value = true;
};
const closeModal = () => {
showModal.value = false;
};
const saveItem = async () => {
editError.value = '';
try {
if (isEditing.value) {
await update${componentName}(editableItem.value.${tableInfo.primaryKey}, editableItem.value);
} else {
await create${componentName}(editableItem.value);
}
closeModal();
fetchItems();
alert('操作成功!');
} catch (error) {
console.error('保存失败:', error);
editError.value = error.response?.data?.message || '操作失败,请重试。';
}
};
const confirmDeleteItem = async (id) => {
if (window.confirm('确定要删除此项吗?')) {
try {
await delete${componentName}(id);
fetchItems();
alert('删除成功!');
} catch (error) {
console.error('删除失败:', error);
alert('删除失败: ' + (error.response?.data?.message || '请稍后重试'));
}
}
};
//
const formatDate = (dateString) => {
if (!dateString) return 'N/A';
try {
const date = new Date(dateString);
return isNaN(date.getTime()) ? dateString : date.toLocaleString();
} catch (e) {
return dateString;
}
};
</sc' + 'ript>
<style scoped>
${styleContent}
</style>
`;
</style>`;
};
const generateCode = () => {
if (!sqlInput.value.trim()) {
parsedTableInfo.value = null;
generatedCode.value = '';
generatedApiCode.value = '';
return;
}
@ -568,26 +633,37 @@ const generateCode = () => {
if (tableInfo) {
generatedCode.value = generateVueComponent(tableInfo);
generatedFileName.value = `${toPascalCase(tableInfo.tableName)}.vue`;
generatedApiCode.value = generateApiFile(tableInfo);
generatedApiFileName.value = `${tableInfo.tableName}.js`;
parsedTableInfo.value = tableInfo;
} else {
generatedCode.value = '';
generatedFileName.value = '';
generatedApiCode.value = '';
generatedApiFileName.value = '';
parsedTableInfo.value = null;
}
};
const downloadCode = () => {
if (!generatedCode.value) return;
const blob = new Blob([generatedCode.value], { type: 'text/plain;charset=utf-8' });
const downloadFile = (filename, content) => {
if (!content) return;
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
const href = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = href;
link.download = generatedFileName.value;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(href);
}
const downloadCode = () => {
downloadFile(generatedFileName.value, generatedCode.value);
};
const downloadApiFile = () => {
downloadFile(generatedApiFileName.value, generatedApiCode.value);
};
</script>
@ -681,6 +757,14 @@ textarea {
background-color: #0069d9;
}
.download-api-button {
background-color: #28a745;
}
.download-api-button:hover {
background-color: #218838;
}
.action-button:disabled {
background-color: #ccc;
cursor: not-allowed;