💄 主页面组件

This commit is contained in:
bunny 2025-06-25 21:48:07 +08:00
parent f8467610f6
commit 0cd9129efa
4 changed files with 353 additions and 1 deletions

View File

@ -3,7 +3,7 @@ server:
spring:
profiles:
active: prod
active: dev
application:
name: generator-code
thymeleaf:

View File

@ -0,0 +1,16 @@
// 如果一开始定义过了 defineComponent 就不要在下 script 标签中再写了
const {defineComponent} = Vue;
// 定义 Header 组件
const AppHeader = defineComponent({
name: "AppHeader",
template: `
<div class="text-center mb-4">
<h2 class="text-primary fw-bold">
<i class="bi bi-code-square me-2"></i>
代码生成器
</h2>
<p class="text-muted">快速生成数据库表对应的代码</p>
</div>
`
});

View File

@ -0,0 +1,194 @@
const MainCard = defineComponent({
name: "MainCard",
props: {
rawTableList: {
type: Array,
default: () => []
},
},
data() {
return {
// 选择的数据库名称
dbName: ref(""),
// 输入的表名
tableName: ref(""),
// 查询数据库加载
dbLoading: ref(false),
// 数据库信息
databaseInfo: ref({}),
// 所有数据库列表
databaseList: ref([]),
}
},
template: `
<div class="card shadow-sm">
<!-- 卡片头部 -->
<div class="card-header bg-primary bg-opacity-10 border-bottom">
<div class="d-flex justify-content-between align-items-center">
<div>
<h5 class="card-title mb-0">
<i class="bi bi-info-circle-fill text-primary me-2"></i>使
</h5>
</div>
<div>
<span class="badge bg-primary rounded-pill me-2">
<i class="bi bi-database me-1"></i>
数据库: <span class="fw-normal">{{databaseList.length}}</span>
</span>
<span class="badge bg-primary rounded-pill">
<i class="bi bi-table me-1"></i>
数据库表: <span class="fw-normal">{{rawTableList.length}}</span>
</span>
</div>
</div>
<p class="card-subtitle mt-2 text-muted">
<i class="bi bi-mouse me-1"></i>
点击 <code class="bg-primary bg-opacity-10">表名</code>
<code class="bg-primary bg-opacity-10">生成</code>
</p>
</div>
<!-- 卡片主体 -->
<div class="card-body">
<div class="d-flex flex-wrap align-items-center gap-3">
<!-- GitHub 链接 -->
<a class="btn btn-outline-dark d-flex align-items-center gap-2"
href="https://github.com/BunnyMaster/generator-code-server" target="_blank">
<svg class="feather feather-github" fill="none" height="20" stroke="currentColor"
stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg">
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
</svg>
GitHub 仓库
</a>
<!-- Gitee 链接 -->
<a class="btn btn-outline-danger d-flex align-items-center gap-2"
href="https://gitee.com/BunnyBoss/generator-code-server" target="_blank">
<svg height="20" viewBox="0 0 24 24" width="20" xmlns="http://www.w3.org/2000/svg">
<path d="M11.984 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12a12 12 0 0 0 12-12A12 12 0 0 0 12 0zm6.09 5.333c.328 0 .593.266.592.593v1.482a.594.594 0 0 1-.593.592H9.777c-.982 0-1.778.796-1.778 1.778v5.63c0 .327.266.592.593.592h5.63c.982 0 1.778-.796 1.778-1.778v-.296a.593.593 0 0 0-.592-.593h-4.15a.59.59 0 0 1-.592-.592v-1.482a.593.593 0 0 1 .593-.592h6.815c.327 0 .593.265.593.592v3.408a4 4 0 0 1-4 4H5.926a.593.593 0 0 1-.593-.593V9.778a4.444 4.444 0 0 1 4.445-4.444h8.296Z"
fill="currentColor"/>
</svg>
Gitee 仓库
</a>
</div>
</div>
<!-- 卡片底部 - 数据库选择 -->
<div class="card-footer bg-light">
<div class="row g-3 align-items-end">
<div class="col-md-5">
<label class="form-label fw-semibold" for="databaseSelect">
<i class="bi bi-database me-1"></i>
</label>
<select class="form-select shadow-sm" id="databaseSelect" v-model="dbName">
<option disabled selected>请选择数据库...</option>
<option :key="index" :title="db.comment" :value="db.tableCat"
v-for="(db,index) in databaseList">
{{db.tableCat}}
</option>
</select>
</div>
<div class="col-md-5">
<label class="form-label fw-semibold" for="tableSelect">
<i class="bi bi-table me-1"></i>
数据库表选择
</label>
<input class="form-control shadow-sm" id="tableSelect" placeholder="输入表名或表注释"
v-model="tableName"/>
</div>
<div class="col-md-2 d-grid">
<button class="btn btn-primary shadow-sm" disabled type="button"
v-if="dbLoading">
<span aria-hidden="true" class="spinner-grow spinner-grow-sm"></span>
<span role="status">Loading...</span>
</button>
<button @click="onRefresh" class="btn btn-primary shadow-sm" v-else>
<i class="bi bi-search me-1"></i>
查询
</button>
</div>
</div>
<div class="pt-1 bg-light">
<a class="d-flex align-items-center text-decoration-none" data-bs-toggle="collapse"
href="#dbInfoCollapse">
<i class="bi bi-database me-2"></i>
<span>数据库连接详情</span>
<i class="bi bi-chevron-down ms-auto"></i>
</a>
<div class="collapse mt-2" id="dbInfoCollapse">
<div class="card card-body bg-white">
<ul class="list-unstyled mb-0">
<li class="mb-2">
<strong>数据库:</strong> {{databaseInfo.databaseProductName}}
{{databaseInfo.databaseProductVersion}}
</li>
<li class="mb-2">
<strong>驱动:</strong> {{databaseInfo.driverName}}
({{databaseInfo.driverVersion}})
</li>
<li class="mb-2"><strong>URL:</strong>
<code class="d-block text-break">{{databaseInfo.url}}</code>
</li>
<li class="mb-2"><strong>用户:</strong> {{databaseInfo.username}}</li>
<li><strong>当前库:</strong> {{dbName}}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
`,
methods: {
/* 当前连接的数据库信息 */
async getDatabaseInfoMetaData() {
this.dbLoading = true;
const response = await axios.get("/table/databaseInfoMetaData");
const {data, code} = response.data;
if (code !== 200) return;
this.databaseInfo = data
// 设置返回的所有的数据库列表
this.databaseList = data.databaseList;
// 设置当前的数据库,为空时赋值,否则就用自己选择的
if (!this.dbName) {
this.dbName = data.currentDatabase;
}
this.dbLoading = false;
},
/* 刷新查询 */
async onRefresh() {
await this.getDatabaseInfoMetaData();
this.$emit("getDatabaseTableList")
}
},
async mounted() {
// 当前连接的数据库信息
await this.getDatabaseInfoMetaData();
},
watch: {
/* 绑定到外部的变量---dbSelect */
dbName: {
handler(val) {
this.$emit("update:dbSelect", val)
},
},
/* 绑定到外部的变量---tableSelect */
tableName: {
handler(val) {
this.$emit("update:tableSelect", val)
}
}
}
})

View File

@ -0,0 +1,142 @@
const MainTable = defineComponent({
name: "MainTable",
props: ["tableList", "rawTableList", "loading"],
template: `
<div class="card mt-4 shadow-sm">
<div class="card-header bg-primary bg-opacity-10">
<h5 class="card-title mb-0">
<i class="bi bi-table me-2"></i>
</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<!-- 加载中... -->
<div class="p-5 text-center" v-if="loading">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<!-- 展示当前数据库所有的表 -->
<table class="table table-striped table-bordered table-hover mb-0" v-else>
<thead class="table-light">
<tr>
<th scope="col" width="5%">#</th>
<th scope="col" width="30%">表名</th>
<th scope="col" width="35%">注释</th>
<th scope="col" width="20%">所属数据库</th>
<th class="text-center" scope="col" width="10%">操作</th>
</tr>
</thead>
<tbody>
<tr :key="index" v-for="(table,index) in paginatedTableList">
<th scope="row">{{index + 1}}</th>
<td><a class="text-decoration-none" href="#">{{ table.tableName}}</a></td>
<td>{{ table.comment }}</td>
<td>{{ table.tableCat }}</td>
<td class="text-center">
<button class="btn btn-sm btn-outline-primary">
<i class="bi bi-gear"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 在表格显示卡片底部添加分页 -->
<div class="card-footer bg-light">
<div class="d-flex justify-content-between align-items-center">
<div class="form-text"> {{ totalItems }} </div>
<nav aria-label="Page navigation">
<ul class="pagination mb-0">
<li :class="{ disabled: currentPage === 1 }" class="page-item">
<a @click.prevent="currentPage = 1" class="page-link" href="#">
<i class="bi bi-chevron-double-left"></i>
</a>
</li>
<li :class="{ disabled: currentPage === 1 }" class="page-item">
<a @click.prevent="currentPage--" class="page-link" href="#">
<i class="bi bi-chevron-left"></i>
</a>
</li>
<!-- 显示页码 -->
<li :class="{ active: currentPage === page }" :key="page" class="page-item"
v-for="page in visiblePages">
<a @click.prevent="currentPage = page" class="page-link" href="#">{{ page }}</a>
</li>
<li :class="{ disabled: currentPage === totalPages }" class="page-item">
<a @click.prevent="currentPage++" class="page-link" href="#">
<i class="bi bi-chevron-right"></i>
</a>
</li>
<li :class="{ disabled: currentPage === totalPages }" class="page-item">
<a @click.prevent="currentPage = totalPages" class="page-link" href="#">
<i class="bi bi-chevron-double-right"></i>
</a>
</li>
</ul>
</nav>
<div class="dropdown">
<button aria-expanded="false" class="btn btn-outline-secondary dropdown-toggle"
data-bs-toggle="dropdown" id="itemsPerPageDropdown" type="button">
每页 {{ itemsPerPage }}
</button>
<ul aria-labelledby="itemsPerPageDropdown" class="dropdown-menu">
<li :key="index" v-for="(table,index) in tablePageOptions">
<a @click.prevent="itemsPerPage = table" class="dropdown-item" href="JavaScript:">
{{table}} /
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
`,
data() {
return {
// 分页相关数据
currentPage: ref(1),
// 每页数
itemsPerPage: ref(10),
// 总的数据条数
totalItems: ref(0),
// 表格选项
tablePageOptions: [5, 10, 20, 50, 100, 150, 200]
}
},
computed: {
// 计算总页数
totalPages() {
return Math.ceil(this.totalItems / this.itemsPerPage);
},
// 分页后的数据
paginatedTableList() {
const start = (this.currentPage - 1) * this.itemsPerPage;
const end = start + this.itemsPerPage;
return this.tableList.slice(start, end);
}
},
methods: {
/* 计算可见的页码范围 */
visiblePages() {
// 显示当前页前后各2页
const range = 2;
const start = Math.max(1, this.currentPage - range);
const end = Math.min(this.totalPages, this.currentPage + range);
const pages = [];
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
}
}
})