添加Sql页面生成;Sql页面逻辑完成

This commit is contained in:
Bunny 2025-07-02 17:14:46 +08:00
parent d13451c1f9
commit 395ec89666
8 changed files with 717 additions and 92 deletions

View File

@ -1,4 +1,4 @@
const DatabaseGeneratorPage = defineComponent({ const AppGeneratorPage = defineComponent({
name: "MainGeneratorPage", name: "MainGeneratorPage",
template: ` template: `
<div class="offcanvas offcanvas-start" data-bs-scroll="false" tabindex="-1" ref="offcanvasElementRef"> <div class="offcanvas offcanvas-start" data-bs-scroll="false" tabindex="-1" ref="offcanvasElementRef">

View File

@ -4,4 +4,4 @@
/* 引入Highlight.js的CSS */ /* 引入Highlight.js的CSS */
@import url("highlight/atom-one-dark.min.css"); @import url("highlight/atom-one-dark.min.css");
/* 添加自定义样式 */ /* 添加自定义样式 */
@import url("./style/style.css"); @import url("./style/style.css");

View File

@ -18,4 +18,46 @@
.offcanvas.offcanvas-start { .offcanvas.offcanvas-start {
width: 100%; width: 100%;
} }
}
/* 添加自定义样式 */
.database-info-card {
border-left: 4px solid #0d6efd;
margin-top: 20px;
}
.table-info-section {
background-color: #f8f9fa;
border-radius: 5px;
padding: 15px;
margin-bottom: 20px;
}
.column-list {
max-height: 500px;
overflow-y: auto;
}
.column-item {
border-left: 3px solid #6c757d;
margin-bottom: 10px;
transition: all 0.3s;
}
.column-item:hover {
border-left-color: #0d6efd;
background-color: #f8f9fa;
}
.badge-java {
background-color: #5382a1;
}
.badge-jdbc {
background-color: #4479a1;
}
.badge-js {
background-color: #f7df1e;
color: #000;
} }

View File

@ -63,93 +63,93 @@ const DatabaseForm = {
</div> </div>
</div> </div>
<!-- 已选择的表 -->
<div class="col-md-12" v-show="form.tableNames.length > 0"> <div class="col-md-12" v-show="form.tableNames.length > 0">
<label class="form-label fw-medium" for="tableNames">已选择的要生成表({{form.tableNames.length}})</label> <label class="form-label fw-medium" for="tableNames">已选择的要生成表({{form.tableNames.length}})</label>
<span class="badge rounded-pill text-bg-dark me-1" v-for="(item,index) in form.tableNames" :ket="index">{{item}}</span> <span class="badge rounded-pill text-bg-dark me-1" v-for="(item,index) in form.tableNames" :ket="index">{{item}}</span>
</div> </div>
<!-- 前端模板选择区域 --> <!-- 前端模板选择区域 -->
<div class="col-12 mt-3 mb-3 p-3 bg-light rounded"> <div class="col-12 mt-3 mb-3 p-3 bg-light rounded">
<label class="form-check-inline col-form-label fw-medium">生成前端模板</label> <label class="form-check-inline col-form-label fw-medium">生成前端模板</label>
<div class="form-check form-check-inline" v-for="(web,index) in webList" :key="index"> <div class="form-check form-check-inline" v-for="(web,index) in webList" :key="index">
<input class="form-check-input border-secondary" :class="{ 'is-invalid': errors.webTemplates }" <input class="form-check-input border-secondary" :class="{ 'is-invalid': errors.webTemplates }"
:id="web.id" type="checkbox" v-model="web.checked" :id="web.id" type="checkbox" v-model="web.checked"
@change="validateTemplates"> @change="validateTemplates">
<label class="form-check-label" :for="web.id" :title="web.name">{{web.label}}</label> <label class="form-check-label" :for="web.id" :title="web.name">{{web.label}}</label>
</div>
<div class="form-check form-check-inline btn-group ms-2">
<button class="btn btn-outline-primary btn-sm" type="button"
@click="onTableSelectAll(webList)">全选</button>
<button class="btn btn-outline-secondary btn-sm" type="button"
@click="onTableInvertSelection(webList)">反选</button>
<button class="btn btn-outline-danger btn-sm" type="button"
@click="onTableClearAll(webList)">全不选</button>
</div>
<div v-if="errors.webTemplates" class="invalid-feedback d-block">
{{ errors.webTemplates }}
</div>
</div> </div>
<div class="form-check form-check-inline btn-group ms-2">
<!-- 后端模板选择区域 --> <button class="btn btn-outline-primary btn-sm" type="button"
<div class="col-12 mt-2 mb-3 p-3 bg-light rounded"> @click="onTableSelectAll(webList)">全选</button>
<label class="form-check-inline col-form-label fw-medium">生成后端模板</label> <button class="btn btn-outline-secondary btn-sm" type="button"
<div class="form-check form-check-inline" v-for="(server,index) in serverList" :key="index"> @click="onTableInvertSelection(webList)">反选</button>
<input class="form-check-input border-secondary" <button class="btn btn-outline-danger btn-sm" type="button"
:class="{ 'is-invalid': errors.serverTemplates }" :id="server.id" type="checkbox" @click="onTableClearAll(webList)">全不选</button>
v-model="server.checked" @change="validateTemplates">
<label class="form-check-label" :title="server.name" :for="server.id">{{server.label}}</label>
</div>
<div class="form-check form-check-inline btn-group ms-2">
<button class="btn btn-outline-primary btn-sm" type="button"
@click="onTableSelectAll(serverList)">全选</button>
<button class="btn btn-outline-secondary btn-sm" type="button"
@click="onTableInvertSelection(serverList)">反选</button>
<button class="btn btn-outline-danger btn-sm" type="button"
@click="onTableClearAll(serverList)">全不选</button>
</div>
<div v-if="errors.serverTemplates" class="invalid-feedback d-block">
{{ errors.serverTemplates }}
</div>
</div> </div>
<div v-if="errors.webTemplates" class="invalid-feedback d-block">
<!-- 操作按钮区域 --> {{ errors.webTemplates }}
<div class="row mt-4">
<div class="col-md-4 btn-group">
<button class="btn btn-outline-primary" data-bs-title="选择数据表中所有的内容" data-bs-toggle="tooltip"
type="button" @click="onSelectAll">
全部选择
</button>
<button class="btn btn-outline-secondary" data-bs-title="将选择的表格内容反向选择" data-bs-toggle="tooltip"
type="button" @click="onInvertSelection">
全部反选
</button>
<button class="btn btn-outline-danger" data-bs-title="取消全部已经选择的数据表" data-bs-toggle="tooltip"
type="button" @click="onClearAll">
全部取消
</button>
</div>
<div class="col-md-4 btn-group">
<button class="btn btn-primary shadow-sm" disabled type="button"
v-if="generatorCodeLoading">
<span aria-hidden="true" class="spinner-grow spinner-grow-sm"></span>
<span role="status">生成选中表...</span>
</button>
<button v-else class="btn btn-primary" type="submit">
生成选中表
</button>
<button class="btn btn-warning" type="button" data-bs-title="取消全部已经选择的数据表" data-bs-toggle="tooltip"
@click="onClearGeneratorData">清空生成记录</button>
</div>
<div class="col-md-4 d-grid gap-2">
<button class="btn btn-primary shadow-sm" disabled type="button"
v-if="downloadLoading">
<span aria-hidden="true" class="spinner-grow spinner-grow-sm"></span>
<span role="status">下载ZIP...</span>
</button>
<button v-else class="btn btn-primary text-white" type="button" @click="onDownloadZip">下载ZIP</button>
</div>
</div> </div>
</div>
<!-- 后端模板选择区域 -->
<div class="col-12 mt-2 mb-3 p-3 bg-light rounded">
<label class="form-check-inline col-form-label fw-medium">生成后端模板</label>
<div class="form-check form-check-inline" v-for="(server,index) in serverList" :key="index">
<input class="form-check-input border-secondary"
:class="{ 'is-invalid': errors.serverTemplates }" :id="server.id" type="checkbox"
v-model="server.checked" @change="validateTemplates">
<label class="form-check-label" :title="server.name" :for="server.id">{{server.label}}</label>
</div>
<div class="form-check form-check-inline btn-group ms-2">
<button class="btn btn-outline-primary btn-sm" type="button"
@click="onTableSelectAll(serverList)">全选</button>
<button class="btn btn-outline-secondary btn-sm" type="button"
@click="onTableInvertSelection(serverList)">反选</button>
<button class="btn btn-outline-danger btn-sm" type="button"
@click="onTableClearAll(serverList)">全不选</button>
</div>
<div v-if="errors.serverTemplates" class="invalid-feedback d-block">
{{ errors.serverTemplates }}
</div>
</div>
<!-- 操作按钮区域 -->
<div class="row mt-4">
<div class="col-md-4 btn-group">
<button class="btn btn-outline-primary" data-bs-title="选择数据表中所有的内容" data-bs-toggle="tooltip"
type="button" @click="onSelectAll">
全部选择
</button>
<button class="btn btn-outline-secondary" data-bs-title="将选择的表格内容反向选择" data-bs-toggle="tooltip"
type="button" @click="onInvertSelection">
全部反选
</button>
<button class="btn btn-outline-danger" data-bs-title="取消全部已经选择的数据表" data-bs-toggle="tooltip"
type="button" @click="onClearAll">
全部取消
</button>
</div>
<div class="col-md-4 btn-group">
<button class="btn btn-primary shadow-sm" disabled type="button"
v-if="generatorCodeLoading">
<span aria-hidden="true" class="spinner-grow spinner-grow-sm"></span>
<span role="status">生成选中表...</span>
</button>
<button v-else class="btn btn-primary" type="submit">
生成选中表
</button>
<button class="btn btn-warning" type="button" @click="onClearGeneratorData">清空生成记录</button>
</div>
<div class="col-md-4 d-grid gap-2">
<button class="btn btn-primary shadow-sm" disabled type="button"
v-if="downloadLoading">
<span aria-hidden="true" class="spinner-grow spinner-grow-sm"></span>
<span role="status">下载ZIP...</span>
</button>
<button v-else class="btn btn-primary text-white" type="button" @click="onDownloadZip">下载ZIP</button>
</div>
</div>
</form> </form>
</div> </div>
</div> </div>
@ -289,7 +289,7 @@ const DatabaseForm = {
this.downloadLoading = true; this.downloadLoading = true;
try { try {
const response = await axiosInstance({ const response = await axiosInstance({
url: "/vms/downloadByZip", url: "/generator/downloadByZip",
method: "POST", method: "POST",
data: this.form, data: this.form,
responseType: 'blob' // 重要指定响应类型为blob responseType: 'blob' // 重要指定响应类型为blob

View File

@ -0,0 +1,413 @@
const SqlForm = {
name: "MainForm",
template: `
<div class="card shadow-sm mt-2 bg-body-secondary">
<!-- 表单标题和折叠控制 -->
<div class="card-header p-3">
<a aria-controls="generatorFormCollapse" aria-expanded="false"
class="d-flex align-items-center text-decoration-none" data-bs-toggle="collapse"
href="#generatorFormCollapse">
<i class="bi bi-pencil me-2"></i>
<span class="fw-semibold">填写生成表单信息</span>
<i class="bi bi-chevron-down ms-auto transition-transform rotate-180"></i>
</a>
</div>
<!-- 表单内容区域 -->
<div class="collapse" :class="{ 'show': defaultCollapse }" id="generatorFormCollapse">
<form class="card-body row" @submit.prevent="handleSubmit" novalidate>
<!-- 基本信息输入区域 -->
<div class="col-md-4 mb-3 has-validation">
<label class="form-label fw-medium" for="author">作者名称</label>
<input class="form-control border-secondary" :class="{ 'is-invalid': errors.author }"
id="author" placeholder="输入作者名称" v-model="form.author" type="text"
@input="validateField('author')">
<div class="invalid-feedback">
{{ errors.author || '请输入作者名称' }}
</div>
</div>
<div class="col-md-4 mb-3 has-validation">
<label class="form-label fw-medium" for="requestMapping">requestMapping名称</label>
<input class="form-control border-secondary" :class="{ 'is-invalid': errors.requestMapping }"
id="requestMapping" placeholder="输入requestMapping名称" v-model="form.requestMapping" type="text"
@input="validateField('requestMapping')">
<div class="invalid-feedback">
{{ errors.requestMapping || '请输入requestMapping名称' }}
</div>
</div>
<div class="col-md-4 mb-3 has-validation">
<label class="form-label fw-medium" for="packageName">包名称</label>
<input class="form-control border-secondary" :class="{ 'is-invalid': errors.packageName }"
id="packageName" placeholder="输入包名称" v-model="form.packageName" type="text"
@input="validateField('packageName')">
<div class="invalid-feedback">
{{ errors.packageName || '请输入包名称' }}
</div>
</div>
<div class="col-md-4 mb-3 has-validation">
<label class="form-label fw-medium" for="simpleDateFormat">时间格式</label>
<input class="form-control border-secondary" :class="{ 'is-invalid': errors.simpleDateFormat }"
id="simpleDateFormat" placeholder="输入时间格式" v-model="form.simpleDateFormat" type="text"
@input="validateField('simpleDateFormat')">
<div class="invalid-feedback">
{{ errors.simpleDateFormat || '请输入时间格式' }}
</div>
</div>
<div class="col-md-4 mb-3 has-validation">
<label class="form-label fw-medium" for="tablePrefixes">去除开头前缀</label>
<input class="form-control border-secondary" :class="{ 'is-invalid': errors.tablePrefixes }"
id="tablePrefixes" placeholder="去除开头前缀" v-model="form.tablePrefixes" type="text"
@input="validateField('tablePrefixes')">
<div class="invalid-feedback">
{{ errors.tablePrefixes || '请输入去除开头前缀' }}
</div>
</div>
<div class="col-md-12 mb-3 has-validation">
<label class="form-label fw-medium" for="sql">Sql语句</label>
<textarea class="form-control border-secondary" style="height: 150px;" :class="{ 'is-invalid': errors.sql }"
id="sql" placeholder="请输入Sql语句" v-model="form.sql" @input="validateField('sql')"/>
<div class="invalid-feedback">
{{ errors.sql || '请输入Sql语句' }}
</div>
</div>
<!-- 前端模板选择区域 -->
<div class="col-12 mt-3 mb-3 p-3 bg-light rounded">
<label class="form-check-inline col-form-label fw-medium">生成前端模板</label>
<div class="form-check form-check-inline" v-for="(web,index) in webList" :key="index">
<input class="form-check-input border-secondary" :class="{ 'is-invalid': errors.webTemplates }"
:id="web.id" type="checkbox" v-model="web.checked"
@change="validateTemplates">
<label class="form-check-label" :for="web.id" :title="web.name">{{web.label}}</label>
</div>
<div class="form-check form-check-inline btn-group ms-2">
<button class="btn btn-outline-primary btn-sm" type="button"
@click="onTableSelectAll(webList)">全选</button>
<button class="btn btn-outline-secondary btn-sm" type="button"
@click="onTableInvertSelection(webList)">反选</button>
<button class="btn btn-outline-danger btn-sm" type="button"
@click="onTableClearAll(webList)">全不选</button>
</div>
<div v-if="errors.webTemplates" class="invalid-feedback d-block">
{{ errors.webTemplates }}
</div>
</div>
<!-- 后端模板选择区域 -->
<div class="col-12 mt-2 mb-3 p-3 bg-light rounded">
<label class="form-check-inline col-form-label fw-medium">生成后端模板</label>
<div class="form-check form-check-inline" v-for="(server,index) in serverList" :key="index">
<input class="form-check-input border-secondary"
:class="{ 'is-invalid': errors.serverTemplates }" :id="server.id" type="checkbox"
v-model="server.checked" @change="validateTemplates">
<label class="form-check-label" :title="server.name" :for="server.id">{{server.label}}</label>
</div>
<div class="form-check form-check-inline btn-group ms-2">
<button class="btn btn-outline-primary btn-sm" type="button"
@click="onTableSelectAll(serverList)">全选</button>
<button class="btn btn-outline-secondary btn-sm" type="button"
@click="onTableInvertSelection(serverList)">反选</button>
<button class="btn btn-outline-danger btn-sm" type="button"
@click="onTableClearAll(serverList)">全不选</button>
</div>
<div v-if="errors.serverTemplates" class="invalid-feedback d-block">
{{ errors.serverTemplates }}
</div>
</div>
<!-- 操作按钮区域 -->
<div class="row mt-4">
<div class="col-md-4 btn-group">
<button class="btn btn-outline-primary" data-bs-title="选择数据表中所有的内容" data-bs-toggle="tooltip"
type="button" @click="onSelectAll">
全部选择
</button>
<button class="btn btn-outline-secondary" data-bs-title="将选择的表格内容反向选择" data-bs-toggle="tooltip"
type="button" @click="onInvertSelection">
全部反选
</button>
<button class="btn btn-outline-danger" data-bs-title="取消全部已经选择的数据表" data-bs-toggle="tooltip"
type="button" @click="onClearAll">
全部取消
</button>
</div>
<div class="col-md-4 btn-group">
<button class="btn btn-primary shadow-sm" disabled type="button"
v-if="generatorCodeLoading">
<span aria-hidden="true" class="spinner-grow spinner-grow-sm"></span>
<span role="status">生成中...</span>
</button>
<button v-else class="btn btn-primary" type="submit">
开始生成
</button>
<button class="btn btn-info" type="button" @click="onGetSqlParserInfo">获取Sql内容信息</button>
<button class="btn btn-warning" type="button" @click="onClearGeneratorData">清空生成记录</button>
</div>
<div class="col-md-4 d-grid gap-2">
<button class="btn btn-primary shadow-sm" disabled type="button"
v-if="downloadLoading">
<span aria-hidden="true" class="spinner-grow spinner-grow-sm"></span>
<span role="status">下载ZIP...</span>
</button>
<button v-else class="btn btn-primary text-white" type="button" @click="onDownloadZip">下载ZIP</button>
</div>
</div>
</form>
</div>
</div>
`,
props: {
// 表单数据对象,包含生成代码所需的各种参数
form: {type: Object, required: true},
// 生成代码的回调函数
onGeneratorCode: {type: Function, required: true},
// 清空生成记录
onClearGeneratorData: {type: Function, required: true},
// 生成代码加载
generatorCodeLoading: {type: Boolean, required: true},
// sql语句中表信息
tableInfo: {type: Object, required: true},
// sql语句中列信息
columnMetaList: {type: Object, required: true},
},
data() {
return {
// 控制表单默认是否展开
defaultCollapse: true,
// 后端模板选项列表
serverList: ref([]),
// 前端模板选项列表
webList: ref([]),
// 错误信息对象
errors: {
author: '',
requestMapping: '',
packageName: '',
simpleDateFormat: '',
tablePrefixes: '',
webTemplates: '',
serverTemplates: '',
sql: ''
},
downloadLoading: ref(false),
};
},
methods: {
/**
* 验证表单字段
* @param {string} field - 字段名
*/
validateField(field) {
if (!this.form[field] || this.form[field].trim() === '') {
this.errors[field] = '此字段为必填项';
return false;
}
this.errors[field] = '';
return true;
},
/* 验证模板选择 */
validateTemplates() {
// 检查列表是否有一个选中的
const hasWebSelected = this.webList.some(item => item.checked);
const hasServerSelected = this.serverList.some(item => item.checked);
// 列表都没有选中
if (!hasWebSelected && !hasServerSelected) {
this.errors.webTemplates = '请至少选择一个前端或后端模板';
this.errors.serverTemplates = '请至少选择一个前端或后端模板';
return false;
}
// 列表选中让错误提示小时
this.errors.webTemplates = '';
this.errors.serverTemplates = '';
// 发生选择变化时,同时父级更新表单
this.updateForm();
return true;
},
/* 验证整个表单 */
validateForm() {
let isValid = true;
// 验证文本字段
const textFields = ['author', 'requestMapping', 'packageName', 'simpleDateFormat', 'tablePrefixes', "sql"];
textFields.forEach(field => {
if (!this.validateField(field)) {
isValid = false;
}
});
// 验证模板选择
if (!this.validateTemplates()) {
isValid = false;
}
return isValid;
},
/* 更新父级表单 */
updateForm() {
const webList = this.webList.filter(item => item.checked).map(item => item.name);
const serverList = this.serverList.filter(item => item.checked).map(item => item.name);
const newForm = {...this.form, path: [...webList, ...serverList,]};
this.$emit("update:form", newForm);
},
/* 获取Sql内容信息 */
async onGetSqlParserInfo() {
const validate = this.validateForm();
if (!validate) return;
// 设置请求参数
const data = {sql: this.form.sql};
// 获取表信息
const tableInfoResponse = await axiosInstance.post("/sqlParser/tableInfo", data, {headers: {'Content-Type': 'multipart/form-data'}});
if (tableInfoResponse.code === 200) {
this.$emit("update:tableInfo", tableInfoResponse.data)
}
// 获取sql中的列信息
const columnMetaDataResponse = await axiosInstance.post("/sqlParser/columnMetaData", data, {headers: {'Content-Type': 'multipart/form-data'}});
if (columnMetaDataResponse.code === 200) {
this.$emit("update:columnMetaList", columnMetaDataResponse.data)
}
},
/* 处理表单提交 */
handleSubmit() {
if (this.validateForm()) {
this.updateForm();
// 如果验证通过,调用父组件提供的生成代码方法
this.onGeneratorCode();
} else {
// 验证失败,可以在这里添加额外的处理逻辑
antd.message.error("表单验证失败")
}
},
/**
* 获取VMS资源路径列表
* 从服务器获取前端和后端模板的路径列表
* @async
* @returns {Promise<void>}
*/
async getVmsResourcePathList() {
const response = await axiosInstance.get("/vms/vmsResourcePathList");
const {data, code, message} = response;
if (code !== 200) {
antd.message.error(message);
return;
}
// 初始化模板选择状态
this.serverList = data.server.map(item => ({...item, checked: false}));
this.webList = data.web.map(item => ({...item, checked: false}));
},
/**
* 下载Zip文件
* @returns {Promise<void>}
*/
async onDownloadZip() {
this.downloadLoading = true;
try {
const response = await axiosInstance({
url: "/generator/downloadByZip",
method: "POST",
data: this.form,
responseType: 'blob' // 重要指定响应类型为blob
});
// 从响应头中获取文件名
const contentDisposition = response.headers['content-disposition'];
let fileName = 'download.zip';
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(/filename=(.+)/);
if (fileNameMatch && fileNameMatch[1]) {
fileName = fileNameMatch[1];
// 处理可能的编码文件名如UTF-8编码
if (fileName.startsWith("UTF-8''")) {
fileName = decodeURIComponent(fileName.replace("UTF-8''", ''));
}
}
}
// 创建Blob对象
const blob = new Blob([response.data]);
// 创建下载链接
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = fileName;
document.body.appendChild(link);
// 触发点击下载
link.click();
// 清理
window.URL.revokeObjectURL(downloadUrl);
document.body.removeChild(link);
} catch (error) {
console.error('下载失败:', error);
}
this.downloadLoading = false;
},
/**
* 全选指定列表
* @param {Array} list - 要处理的列表
*/
onTableSelectAll(list) {
list.forEach(item => item.checked = true);
this.validateTemplates();
},
/**
* 反选指定列表
* @param {Array} list - 要处理的列表
*/
onTableInvertSelection(list) {
list.forEach(item => item.checked = !item.checked);
this.validateTemplates();
},
/**
* 清空指定列表的选择
* @param {Array} list - 要处理的列表
*/
onTableClearAll(list) {
list.forEach(item => item.checked = false);
this.validateTemplates();
},
/* 全选所有模板 */
onSelectAll() {
this.onTableSelectAll(this.webList);
this.onTableSelectAll(this.serverList);
},
/* 反选所有模板 */
onInvertSelection() {
this.onTableInvertSelection(this.webList);
this.onTableInvertSelection(this.serverList);
},
/* 清空所有模板的选择 */
onClearAll() {
this.onTableClearAll(this.webList);
this.onTableClearAll(this.serverList);
}
},
async mounted() {
// 组件挂载时获取模板列表
await this.getVmsResourcePathList();
},
}

View File

@ -0,0 +1,112 @@
const SqlParserInfo = {
name: "SqlParserInfo",
template: `
<div class="card database-info-card">
<!-- 数据库信息折叠面板 -->
<div class="card-header bg-light d-flex justify-content-between align-items-center" data-bs-toggle="collapse"
:data-bs-target="'#databaseInfoCollapse'" aria-expanded="true" aria-controls="databaseInfoCollapse"
style="cursor: pointer">
<h4 class="mb-0"><i class="fas fa-database me-2"></i></h4>
<i class="fas fa-chevron-down transition-all"></i>
</div>
<div id="databaseInfoCollapse" class="collapse show">
<div class="card-body">
<!-- 表信息折叠面板 -->
<div class="table-info-section mb-3">
<div class="d-flex justify-content-between align-items-center mb-2"
data-bs-toggle="collapse"
:data-bs-target="'#tableInfoCollapse'"
aria-expanded="true"
aria-controls="tableInfoCollapse"
style="cursor: pointer">
<h5 class="mb-0"><i class="fas fa-table me-2"></i></h5>
<i class="fas fa-chevron-down transition-all"></i>
</div>
<div id="tableInfoCollapse" class="collapse show">
<div class="row">
<div class="col-md-6">
<ul class="list-group list-group-flush">
<li class="list-group-item">
<strong>表名:</strong> {{ tableInfo.tableName }}
</li>
<li class="list-group-item">
<strong>注释:</strong> {{ tableInfo.comment || '' }}
</li>
</ul>
</div>
<div class="col-md-6">
<ul class="list-group list-group-flush">
<li class="list-group-item">
<strong>表类型:</strong> {{ tableInfo.tableType || '' }}
</li>
<li class="list-group-item">
<strong>表目录:</strong> {{ tableInfo.tableCat || '' }}
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- 列信息折叠面板 -->
<div>
<div class="d-flex justify-content-between align-items-center mb-2"
data-bs-toggle="collapse"
:data-bs-target="'#columnInfoCollapse'"
aria-expanded="true"
aria-controls="columnInfoCollapse"
style="cursor: pointer">
<h5 class="mb-0"><i class="fas fa-columns me-2"></i> ({{ columnMetaList.length }})</h5>
<i class="fas fa-chevron-down transition-all"></i>
</div>
<div id="columnInfoCollapse" class="collapse show">
<div class="column-list">
<div :key="index" class="card column-item mb-2" v-for="(column, index) in columnMetaList">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<p class="card-text text-muted small mb-2">
{{column.columnName.replace(/\`/g, '')}} {{ column.comment || '无注释' }}
</p>
<span class="badge bg-secondary" v-if="column.isPrimaryKey">主键</span>
</div>
<div class="row">
<div class="col-md-4">
<small class="text-muted">Java类型:</small>
<span class="badge badge-java ms-2">{{ column.javaType }}</span>
</div>
<div class="col-md-4">
<small class="text-muted">JDBC类型:</small>
<span class="badge badge-jdbc ms-2">{{ column.jdbcType }}</span>
</div>
<div class="col-md-4">
<small class="text-muted">JS类型:</small>
<span class="badge badge-js ms-2">{{ column.javascriptType }}</span>
</div>
</div>
<div class="mt-2">
<small class="text-muted">字段名:</small>
<code class="ms-2">{{ column.lowercaseName.replace(/\`/g, '') }}</code>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`,
props: {
// sql语句中表信息
tableInfo: {type: Object, required: true},
// sql语句中列信息
columnMetaList: {type: Object, required: true},
},
data() {
return {};
},
};

View File

@ -45,8 +45,8 @@
:raw-table-list="rawTableList" :table-list="tableList" v-model:form="form"></database-table> :raw-table-list="rawTableList" :table-list="tableList" v-model:form="form"></database-table>
<!-- 模板生成页面 --> <!-- 模板生成页面 -->
<database-generator-page :generator-data="generatorData" ref="mainGeneratorRef" <app-generator-page :generator-data="generatorData"
v-model:generator-page-flag="generatorPageFlag"></database-generator-page> v-model:generator-page-flag="generatorPageFlag"></app-generator-page>
</div> </div>
</div> </div>
</body> </body>
@ -66,7 +66,7 @@
<script th:src="@{/src/views/database/DatabaseCard.js}"></script> <script th:src="@{/src/views/database/DatabaseCard.js}"></script>
<script th:src="@{/src/views/database/DatabaseForm.js}"></script> <script th:src="@{/src/views/database/DatabaseForm.js}"></script>
<script th:src="@{/src/views/database/DatabaseTable.js}"></script> <script th:src="@{/src/views/database/DatabaseTable.js}"></script>
<script th:src="@{/src/views/database/DatabaseGeneratorPage.js}"></script> <script th:src="@{/src/components/AppGeneratorPage.js}"></script>
<script> <script>
const {createApp, ref} = Vue const {createApp, ref} = Vue
@ -143,7 +143,7 @@
if (!isValidate) return; if (!isValidate) return;
// 请求生成模板 // 请求生成模板
const {data, code, message} = await axiosInstance.post("/vms/generator", this.form); const {data, code, message} = await axiosInstance.post("/generator", this.form);
// 判断是否请求成功 // 判断是否请求成功
if (code !== 200) { if (code !== 200) {
@ -164,6 +164,7 @@
/* 清空已经生成的内容 */ /* 清空已经生成的内容 */
onClearGeneratorData() { onClearGeneratorData() {
this.generatorData = {}; this.generatorData = {};
antd.message.success("清除完成");
}, },
}, },
watch: { watch: {
@ -190,7 +191,7 @@
app.component('DatabaseCard', DatabaseCard); app.component('DatabaseCard', DatabaseCard);
app.component('DatabaseForm', DatabaseForm); app.component('DatabaseForm', DatabaseForm);
app.component('DatabaseTable', DatabaseTable); app.component('DatabaseTable', DatabaseTable);
app.component('DatabaseGeneratorPage', DatabaseGeneratorPage); app.component('AppGeneratorPage', AppGeneratorPage);
app.mount('#app'); app.mount('#app');
</script> </script>

View File

@ -32,6 +32,16 @@
<!-- 主标题 --> <!-- 主标题 -->
<app-header></app-header> <app-header></app-header>
<sql-form :generator-code-loading="generatorCodeLoading" :on-clear-generator-data="onClearGeneratorData"
:on-generator-code="onGeneratorCode" ref="mainFormRef" v-model:column-meta-list="columnMetaList"
v-model:form="form" v-model:table-info="tableInfo"></sql-form>
<!-- 数据库信息展示区域 -->
<sql-parser-info :column-meta-list="columnMetaList" :table-info="tableInfo"></sql-parser-info>
<!-- 模板生成页面 -->
<app-generator-page :generator-data="generatorData"
v-model:generator-page-flag="generatorPageFlag"></app-generator-page>
</div> </div>
</div> </div>
</body> </body>
@ -48,22 +58,23 @@
<!-- 引入组件 --> <!-- 引入组件 -->
<script th:src="@{/src/components/AppHeader.js}"></script> <script th:src="@{/src/components/AppHeader.js}"></script>
<script th:src="@{/src/views/sql/SqlForm.js}"></script>
<script th:src="@{/src/views/sql/SqlParserInfo.js}"></script>
<script th:src="@{/src/components/AppGeneratorPage.js}"></script>
<script> <script>
const {createApp, ref} = Vue const {createApp, ref} = Vue
const app = createApp({ const app = createApp({
setup() { setup() {
return { return {
loading: ref(false), // 是否正在生成
generatorCodeLoading: ref(false),
// 提交的表单 // 提交的表单
form: ref({ form: ref({
// 作者名称 // 作者名称
author: "Bunny", author: "Bunny",
// requestMapping名称 // requestMapping名称
requestMapping: "/api", requestMapping: "/api",
// 表名称
tableNames: [],
// 包名称 // 包名称
packageName: "cn.bunny", packageName: "cn.bunny",
// 时间格式 // 时间格式
@ -72,16 +83,62 @@
tablePrefixes: "t_,sys_,qrtz_,log_", tablePrefixes: "t_,sys_,qrtz_,log_",
// 生成代码路径 // 生成代码路径
path: [], path: [],
sql: "",
}), }),
// 是否显示生成页面
generatorPageFlag: ref(false),
// 生成的数据 // 生成的数据
generatorData: ref({}), generatorData: ref({}),
// sql语句中表信息
tableInfo: ref({}),
// sql语句中列信息
columnMetaList: ref([]),
}; };
}, },
methods: {}, methods: {
/**
* 生成代码
* @returns {Promise<void>}
*/
async onGeneratorCode() {
this.generatorCodeLoading = true;
// 验证表单是否通过
const isValidate = this.$refs.mainFormRef.validateForm();
if (!isValidate) return;
// 请求生成模板
const {data, code, message} = await axiosInstance.post("/generator", this.form);
// 判断是否请求成功
if (code !== 200) {
this.generatorCodeLoading = false;
return;
} else antd.message.success(message);
// 显示生成的页面
this.generatorData = data;
this.generatorPageFlag = true;
this.generatorCodeLoading = false;
// 等待 DOM 更新,之后手动更新代码高亮
await this.$nextTick();
hljs.highlightAll();
},
/* 清空已经生成的内容 */
onClearGeneratorData() {
this.generatorData = {};
antd.message.success("清除完成")
},
},
}); });
// 注册组件 // 注册组件
app.component('AppHeader', AppHeader); app.component('AppHeader', AppHeader);
app.component('SqlForm', SqlForm);
app.component('SqlParserInfo', SqlParserInfo);
app.component('AppGeneratorPage', AppGeneratorPage);
app.mount('#app'); app.mount('#app');
</script> </script>