Files
GERP/src/views/system/Database.vue
T

264 lines
7.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
/**
* 数据库管理(Database)
*
* 迁移自 TempLate/GERP/pages/system/database.html(166 行)
*
* 原生实现要点:
* - 仅管理员可访问(GERP.CheckAdmin),非管理员显示无权访问
* - 主实例(默认服务器):GET /API/FormAPI/Tables 返回全部表,按 type=static/dynamic 分两组展示
* - 子服务器:GET /api/gerp/msync/servers/{server}/form/?Action=tables 返回该服务器表列表
* - 表项字段:name / title / type(static|dynamic) / count(记录数)
* - 点击"查看"跳转 dbtable.html?server&table&title
*
* Vue 版改动:
* - server / admin_verify 由 request.ts 拦截器统一注入(主实例 FormAPI 路径自动注入)
* - 权限校验改用 userStore.isAdmin 前端预拦截;非管理员显示无权访问占位
* - 子服务器表列表接口路径已含 serverName,admin_verify 仍由拦截器按 URL query 注入
* - 跳转改用 vue-router:/system/dbtable?table=xxx&title=xxx(子服务器追加 server)
* - 返回服务器列表改用 router.push(替代 GERP.Navigate)
*/
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { get } from '@/api/request'
import { setPageTitle, getServer, isDefaultServer } from '@/utils/serverScope'
import { useUserStore } from '@/stores/user'
import AdminBadge from '@/components/AdminBadge.vue'
const router = useRouter()
const userStore = useUserStore()
const isAdmin = computed(() => userStore.isAdmin)
const denied = ref(false)
/** 服务器标识(主实例为 gvsds-master) */
const serverName = getServer()
/** 是否主实例 */
const isMain = isDefaultServer()
/** 表项(对齐后端字段) */
interface TableItem {
name: string
title?: string
type?: 'static' | 'dynamic'
count?: number
}
const loading = ref(false)
/** 静态表列表(仅主实例有分组) */
const staticTables = ref<TableItem[]>([])
/** 系统表 / 子服务器数据表列表 */
const dynamicTables = ref<TableItem[]>([])
/** 页面标题 */
const pageTitle = computed(() => (isMain ? '数据库管理' : `${serverName} — 数据库管理`))
setPageTitle(pageTitle.value)
/** 加载主实例表列表:GET /API/FormAPI/Tables */
async function loadMainTables(): Promise<void> {
loading.value = true
try {
const data = await get<TableItem[]>('/API/FormAPI/Tables')
const tables = Array.isArray(data) ? data : []
staticTables.value = tables.filter((t) => t.type === 'static')
dynamicTables.value = tables.filter((t) => t.type === 'dynamic')
} catch {
/* 拦截器已弹错误 */
} finally {
loading.value = false
}
}
/** 加载子服务器表列表:GET /api/gerp/msync/servers/{server}/form/?Action=tables */
async function loadSubTables(): Promise<void> {
loading.value = true
try {
const data = await get<TableItem[]>(`/api/gerp/msync/servers/${encodeURIComponent(serverName)}/form/`, {
Action: 'tables',
})
dynamicTables.value = Array.isArray(data) ? data : []
} catch {
dynamicTables.value = []
} finally {
loading.value = false
}
}
/** 查看表:跳转 DbTable 页面 */
function viewTable(table: TableItem): void {
const query: Record<string, string> = {
table: table.name,
title: table.title || table.name,
}
if (!isMain) query.server = serverName
router.push({ path: '/system/dbtable', query })
}
/** 返回服务器列表 */
function goBack(): void {
router.push({ path: '/system/msync-servers' })
}
/** 表项展示文本:标题与名称 */
function tableLabel(table: TableItem): string {
return table.title && table.title !== table.name ? `${table.title}(${table.name})` : table.name
}
/** 表项记录数 / 类型描述 */
function tableMeta(table: TableItem): string {
if (typeof table.count === 'number' && table.count >= 0) return `${table.count} 条记录`
return table.type === 'dynamic' ? '系统表' : '自定义表'
}
onMounted(() => {
if (!isAdmin.value) {
denied.value = true
return
}
if (isMain) {
loadMainTables()
} else {
loadSubTables()
}
})
</script>
<template>
<div class="db-page">
<AdminBadge />
<el-empty
v-if="denied"
description="无权访问:该页面仅管理员可查看,请切换至管理员角色或联系管理员"
>
<template #image>
<i class="fas fa-lock" style="font-size: 48px; color: var(--el-color-danger)" />
</template>
</el-empty>
<template v-else>
<!-- 子服务器:返回服务器列表 -->
<div v-if="!isMain" class="db-back" @click="goBack">
<i class="fas fa-arrow-left" /> <span>返回服务器列表</span>
</div>
<!-- 页头 -->
<div class="db-header">
<h1 class="page-title">
<i class="fas fa-database icon-primary" />
<span>{{ pageTitle }}</span>
</h1>
</div>
<!-- 静态表分组(仅主实例) -->
<div v-if="isMain" class="db-section">
<h3>静态表</h3>
<div v-loading="loading" class="db-list">
<div v-for="t in staticTables" :key="t.name" class="db-table-item">
<span class="db-table-name">{{ tableLabel(t) }}</span>
<span class="db-table-meta">{{ tableMeta(t) }}</span>
<el-button size="small" type="primary" @click="viewTable(t)">
<i class="fas fa-table" /> 查看
</el-button>
</div>
<el-empty v-if="!loading && staticTables.length === 0" description="暂无数据" :image-size="60" />
</div>
</div>
<!-- 系统表 / 子服务器数据表分组 -->
<div class="db-section">
<h3>{{ isMain ? '系统表' : '数据表' }}</h3>
<div v-loading="loading" class="db-list">
<div v-for="t in dynamicTables" :key="t.name" class="db-table-item">
<span class="db-table-name">{{ tableLabel(t) }}</span>
<span class="db-table-meta">{{ tableMeta(t) }}</span>
<el-button size="small" type="primary" @click="viewTable(t)">
<i class="fas fa-table" /> 查看
</el-button>
</div>
<el-empty v-if="!loading && dynamicTables.length === 0" description="暂无数据表" :image-size="60" />
</div>
</div>
</template>
</div>
</template>
<style scoped lang="scss">
.db-page {
padding: 20px;
height: 100%;
box-sizing: border-box;
}
.db-back {
cursor: pointer;
color: var(--el-color-primary);
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 14px;
margin-bottom: 16px;
&:hover {
text-decoration: underline;
}
}
.db-header {
margin-bottom: 20px;
.page-title {
margin: 0;
font-size: 20px;
font-weight: 700;
display: flex;
align-items: center;
gap: 8px;
.icon-primary {
color: var(--el-color-primary);
}
}
}
.db-section {
margin-bottom: 30px;
h3 {
margin-bottom: 10px;
font-size: 16px;
}
}
.db-list {
min-height: 60px;
}
.db-table-item {
display: flex;
align-items: center;
padding: 12px 16px;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color);
border-radius: 8px;
margin-bottom: 8px;
transition: all 0.2s;
&:hover {
border-color: var(--el-color-primary);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.db-table-name {
flex: 1;
font-weight: 500;
}
.db-table-meta {
margin-right: 16px;
font-size: 12px;
color: var(--el-text-color-secondary);
}
}
</style>