初始提交:OCI 面板前端(含 GenAI 网关一期)

This commit is contained in:
Wang Defa
2026-07-09 15:31:29 +08:00
commit db45a669e3
135 changed files with 25220 additions and 0 deletions
+174
View File
@@ -0,0 +1,174 @@
<script setup lang="ts">
import {
NButton,
NDataTable,
NInputGroup,
NInputGroupLabel,
NSelect,
type DataTableColumns,
} from 'naive-ui'
import { computed, h, ref, watch } from 'vue'
import { RouterLink } from 'vue-router'
import { listSubnets, listVcns } from '@/api/networks'
import DeadPlaceholder from '@/components/DeadPlaceholder.vue'
import FootNote from '@/components/FootNote.vue'
import LifecycleBadge from '@/components/LifecycleBadge.vue'
import CreateVcnModal from '@/components/network/CreateVcnModal.vue'
import StatusBadge from '@/components/StatusBadge.vue'
import { useRegionAlias } from '@/composables/useRegionAlias'
import { useAsync } from '@/composables/useAsync'
import { useScopeStore } from '@/stores/scope'
import type { Vcn } from '@/types/api'
interface VcnRow extends Vcn {
cfgId: number
region: string
subnetCount: number
}
/** 租户 / 区域 / 区间来自侧栏全局作用域 */
const scope = useScopeStore()
const { alias: regionAlias } = useRegionAlias()
const showCreate = ref(false)
/** 当前作用域租户失联时整页替换占位,不发起资源请求 */
const isDead = computed(() => scope.currentConfig?.aliveStatus === 'dead')
const rows = useAsync<VcnRow[]>(async () => {
const cfg = scope.currentConfig
if (!cfg || cfg.aliveStatus === 'dead') return []
const region = scope.region || undefined
try {
const vcns = await listVcns(cfg.id, region, scope.compartmentId || undefined)
return Promise.all(
vcns.map(async (vcn) => {
// 后端异常路径可能返回 null 而非 [],.catch 拦不住,需再判空
const subnets = (await listSubnets(cfg.id, vcn.id, region).catch(() => [])) ?? []
return {
...vcn,
cfgId: cfg.id,
region: scope.region || cfg.region,
subnetCount: subnets.length,
}
}),
)
} catch {
return []
}
}, false)
watch(
[() => scope.currentConfig?.id, () => scope.region, () => scope.compartmentId],
() => void rows.run(),
{ immediate: true },
)
const columns = computed<DataTableColumns<VcnRow>>(() => [
{
title: 'VCN',
key: 'displayName',
minWidth: 200,
render: (row) =>
h('div', { class: 'min-w-0' }, [
h(
RouterLink,
{
class: 'font-medium text-ink hover:text-accent',
to: {
name: 'vcn-detail',
params: { cfgId: row.cfgId, vcnId: row.id },
query: { region: scope.region || undefined },
},
},
{ default: () => row.displayName },
),
h('div', { class: 'mono text-xs text-ink-3 mt-px' }, row.dnsLabel),
]),
},
{
title: '区域',
key: 'region',
minWidth: 150,
render: (row) => h('span', { class: 'text-[13px]' }, regionAlias(row.region)),
},
{
title: 'IPv4 CIDR',
key: 'cidrBlocks',
minWidth: 130,
render: (row) => h('span', { class: 'mono' }, (row.cidrBlocks ?? []).join(', ')),
},
{
title: 'IPv6',
key: 'ipv6CidrBlocks',
minWidth: 120,
render: (row) =>
row.ipv6CidrBlocks?.length
? h(StatusBadge, { kind: 'prov', label: '/56 已分配', dot: false })
: h(StatusBadge, { kind: 'check', label: '未启用', dot: false }),
},
{
title: '状态',
key: 'lifecycleState',
width: 120,
render: (row) => h(LifecycleBadge, { state: row.lifecycleState }),
},
{
title: '子网',
key: 'subnetCount',
width: 70,
render: (row) => h('span', { class: 'tabular-nums' }, String(row.subnetCount)),
},
])
</script>
<template>
<div class="flex flex-col gap-4 p-6 pb-8 max-md:p-3">
<div class="flex flex-wrap items-center justify-between gap-2">
<div class="flex items-baseline gap-2.5">
<h1 class="page-title">网络</h1>
<span class="text-[13px] text-ink-3">
{{ rows.data.value?.length ?? '…' }} VCN ·
{{ scope.currentConfig?.alias ?? '…' }}
</span>
</div>
<NButton v-if="!isDead" size="small" type="primary" @click="showCreate = true">
创建 VCN
</NButton>
</div>
<DeadPlaceholder v-if="isDead" :last-error="scope.currentConfig?.lastError" />
<div v-else class="panel">
<div class="flex flex-wrap items-center gap-2 px-4.5 py-3">
<NInputGroup class="!w-72 max-md:!w-full">
<NInputGroupLabel size="small">区间</NInputGroupLabel>
<NSelect
v-model:value="scope.compartmentId"
:options="scope.compOptions"
:loading="scope.compartments.loading"
:disabled="scope.compDisabled"
size="small"
/>
</NInputGroup>
</div>
<NDataTable
:columns="columns"
:data="rows.data.value ?? []"
:loading="rows.loading.value"
:scroll-x="920"
:row-key="(r: VcnRow) => r.id"
/>
<FootNote>
创建 VCN 默认启用 IPv6Oracle GUA /56)并将默认安全列表替换为出入全放行;如需收紧请再更新安全列表
</FootNote>
</div>
<CreateVcnModal
v-model:show="showCreate"
:cfg-id="scope.currentConfig?.id ?? null"
:compartment-id="scope.compartmentId"
:region="scope.region"
@created="rows.run()"
/>
</div>
</template>