@@ -11,11 +11,15 @@ import JsonBlock from '@/components/JsonBlock.vue'
|
||||
import StatusBadge from '@/components/StatusBadge.vue'
|
||||
import { eventLabel } from '@/composables/useEventLabel'
|
||||
import { fmtTime } from '@/composables/useFormat'
|
||||
import { useIsMobile } from '@/composables/useIsMobile'
|
||||
import { useMobileModal } from '@/composables/useMobileModal'
|
||||
import type { AuditEvent } from '@/types/api'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
|
||||
const props = defineProps<{ cfgId: number }>()
|
||||
const message = useToast()
|
||||
const isMobile = useIsMobile()
|
||||
const { modalStyle, modalClass } = useMobileModal(720)
|
||||
|
||||
const loading = ref(false)
|
||||
const loadingMore = ref(false)
|
||||
@@ -67,6 +71,10 @@ const pagination = reactive({
|
||||
pageSize: 20,
|
||||
showSizePicker: true,
|
||||
pageSizes: [20, 50, 100],
|
||||
// 移动端页码槽位放不下会被裁切,切 simple 形态(‹ n/总页 ›)
|
||||
get simple() {
|
||||
return isMobile.value
|
||||
},
|
||||
/** 状态行:加载中/尽头给文案;停驻且还有更早数据时给「加载更早」按钮,
|
||||
* 自动补批封顶后由用户显式接力,不会无人值守扫完整个保留期 */
|
||||
prefix: () => {
|
||||
@@ -283,7 +291,6 @@ const columns: DataTableColumns<AuditEvent> = [
|
||||
@clear="submitSearch()"
|
||||
/>
|
||||
</NInputGroup>
|
||||
<NButton size="small" :loading="loading" @click="load()">刷新</NButton>
|
||||
</div>
|
||||
|
||||
<EmptyCard v-if="error" title="审计日志加载失败" :note="error" />
|
||||
@@ -297,14 +304,11 @@ const columns: DataTableColumns<AuditEvent> = [
|
||||
:row-props="rowProps"
|
||||
/>
|
||||
<FootNote>
|
||||
OCI Audit 免费且默认开启,事件保留 365 天,无需在云端做任何配置 ·
|
||||
实时查询不入库,事件通常延迟数分钟可见 ·
|
||||
已默认过滤遥测噪声(SummarizeMetricsData)与内网地址发起的服务互调 ·
|
||||
从最新事件起每批约 200 条,翻到末页自动向更早加载,空档期自动扩窗回溯 ·
|
||||
搜索匹配事件 / 资源 / 操作者等可见字段(不区分大小写,支持 * 通配),命中较少时自动补两批后停驻,点「加载更早」继续回溯 · 点击行查看完整详情
|
||||
事件通常延迟数分钟可见 ·
|
||||
从最新事件起每批约 200 条,翻到末页自动向更早加载,命中较少时自动补两批后停驻
|
||||
</FootNote>
|
||||
|
||||
<NModal v-model:show="showDetail" preset="card" closable :style="{ width: '720px', maxWidth: 'calc(100vw - 24px)' }">
|
||||
<NModal v-model:show="showDetail" preset="card" closable :style="modalStyle" :class="modalClass">
|
||||
<template #header>
|
||||
<DetailHero
|
||||
v-if="detail"
|
||||
|
||||
@@ -178,7 +178,7 @@ function addExemption() {
|
||||
<template>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="panel">
|
||||
<div class="flex items-center justify-between border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2 border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="text-sm font-semibold">SAML 身份提供商</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<NButton size="small" @click="doDownloadMetadata">下载 SP 元数据</NButton>
|
||||
@@ -193,13 +193,12 @@ function addExemption() {
|
||||
:row-key="(r: IdentityProvider) => r.id"
|
||||
/>
|
||||
<FootNote>
|
||||
推荐流程:下载 SP SAML 元数据导入 IdP 侧 → 创建 IdP → 激活(自动显示到登录页)→ 创建免 MFA
|
||||
规则;停用与删除会先将 IdP 移出登录页
|
||||
流程:下载 SP 元数据导入 IdP 侧 → 创建 → 激活上登录页 → 建免 MFA 规则;停用 / 删除自动移出登录页
|
||||
</FootNote>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="flex items-center justify-between border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2 border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="text-sm font-semibold">Sign-on 策略(免 MFA 规则)</div>
|
||||
<NButton size="small" @click="addExemption">为已启用 IdP 创建免 MFA 规则</NButton>
|
||||
</div>
|
||||
|
||||
@@ -8,12 +8,14 @@ import { useToast } from '@/composables/useToast'
|
||||
import { invoiceDate, invoiceStatusMeta } from '@/components/tenant/invoiceStatus'
|
||||
import { useAsync } from '@/composables/useAsync'
|
||||
import { fmtMoney } from '@/composables/useFormat'
|
||||
import { useMobileModal } from '@/composables/useMobileModal'
|
||||
import type { Invoice } from '@/types/api'
|
||||
|
||||
/** 发票明细弹窗:费用行 + 合计 + 付款/PDF 入口 */
|
||||
const props = defineProps<{ show: boolean; cfgId: number; invoice: Invoice | null }>()
|
||||
const emit = defineEmits<{ 'update:show': [boolean]; pay: [Invoice] }>()
|
||||
const message = useToast()
|
||||
const { modalStyle, modalClass } = useMobileModal(640)
|
||||
|
||||
const lines = useAsync(async () => {
|
||||
const inv = props.invoice
|
||||
@@ -51,7 +53,8 @@ async function onPdf() {
|
||||
:show="show"
|
||||
preset="card"
|
||||
:title="invoice ? `发票 ${invoice.number || invoice.id}` : '发票明细'"
|
||||
:style="{ width: '640px', maxWidth: 'calc(100vw - 24px)' }"
|
||||
:style="modalStyle"
|
||||
:class="modalClass"
|
||||
@update:show="emit('update:show', $event)"
|
||||
>
|
||||
<template v-if="invoice">
|
||||
|
||||
@@ -4,11 +4,13 @@ import { ref, watch } from 'vue'
|
||||
|
||||
import { fetchInvoicePdf, saveInvoicePdf } from '@/api/billing'
|
||||
import OfficePreview from '@/components/objectstorage/viewer/OfficePreview.vue'
|
||||
import { useMobileModal } from '@/composables/useMobileModal'
|
||||
import type { Invoice } from '@/types/api'
|
||||
|
||||
/** 发票 PDF 预览弹窗:面板代理拉取后 vue-office 渲染,预览框内可下载 */
|
||||
const props = defineProps<{ show: boolean; cfgId: number; invoice: Invoice | null }>()
|
||||
const emit = defineEmits<{ 'update:show': [boolean] }>()
|
||||
const { modalStyle, modalClass } = useMobileModal(860)
|
||||
|
||||
const loading = ref(false)
|
||||
const loadError = ref('')
|
||||
@@ -49,10 +51,12 @@ function download() {
|
||||
:show="show"
|
||||
preset="card"
|
||||
:title="invoice ? `发票 ${invoice.number || invoice.id} · PDF` : 'PDF 预览'"
|
||||
:style="{ width: '860px', maxWidth: 'calc(100vw - 24px)' }"
|
||||
:style="modalStyle"
|
||||
:class="modalClass"
|
||||
@update:show="emit('update:show', $event)"
|
||||
>
|
||||
<div class="h-[70vh] overflow-auto rounded-lg border border-line-soft bg-wash/50">
|
||||
<!-- 全屏形态下预览区吃满内容区剩余高度(内容区为 flex column) -->
|
||||
<div class="h-[70vh] overflow-auto rounded-lg border border-line-soft bg-wash/50 max-md:h-auto max-md:min-h-0 max-md:flex-1">
|
||||
<div v-if="loading" class="flex h-full items-center justify-center">
|
||||
<NSpin size="small" />
|
||||
</div>
|
||||
|
||||
@@ -296,8 +296,6 @@ const rowProps = (row: Invoice) => ({
|
||||
<div class="flex items-center gap-2.5 border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="text-sm font-semibold">发票</div>
|
||||
<div class="text-xs text-ink-3">已加载 {{ list.length }} 张,点击行查看费用明细</div>
|
||||
<div class="flex-1" />
|
||||
<NButton size="tiny" quaternary @click="init">刷新</NButton>
|
||||
</div>
|
||||
<NDataTable
|
||||
:columns="columns"
|
||||
|
||||
@@ -312,7 +312,7 @@ function policyDesc(p: PasswordPolicy): string {
|
||||
<div class="grid grid-cols-2 items-start gap-4 max-lg:grid-cols-1">
|
||||
<!-- 密码策略与身份 -->
|
||||
<div class="panel">
|
||||
<div class="flex items-center justify-between border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2 border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="text-sm font-semibold">密码策略与身份</div>
|
||||
<div class="text-[12.5px] text-ink-3">Default 身份域 · IAM 规则</div>
|
||||
</div>
|
||||
|
||||
@@ -75,12 +75,12 @@ function subtitle(m: PaymentMethod): string {
|
||||
合约/伙伴结算账号的付款安排请联系 Oracle 销售或查看合同条款
|
||||
</div>
|
||||
<div v-else class="py-8 text-center text-[12.5px] text-ink-3">
|
||||
未登记付款方式(Free Tier 常见);添加卡片请前往 OCI Billing Center
|
||||
未登记付款方式
|
||||
</div>
|
||||
|
||||
<div class="mt-3 rounded-lg bg-wash px-3 py-2.5 text-xs leading-relaxed text-ink-3">
|
||||
添加或更换卡片需经 OCI 托管付款页完成,面板不经手卡号;请在 OCI 控制台「Billing &
|
||||
Cost Management → Payment Methods」操作,完成后回本页刷新即可。
|
||||
加换卡片请在 OCI 控制台「Billing &
|
||||
Cost Management → Payment Methods」操作。
|
||||
</div>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
@@ -12,12 +12,15 @@ import { computed, h, reactive, ref, watch } from 'vue'
|
||||
|
||||
import { listCachedRegions } from '@/api/configs'
|
||||
import { listLimitServices, listLimits } from '@/api/tenant'
|
||||
import FilterBar from '@/components/FilterBar.vue'
|
||||
import FootNote from '@/components/FootNote.vue'
|
||||
import { regionCity, shortAd } from '@/composables/useFormat'
|
||||
import { useAsync } from '@/composables/useAsync'
|
||||
import { useIsMobile } from '@/composables/useIsMobile'
|
||||
import type { LimitItem } from '@/types/api'
|
||||
|
||||
const props = defineProps<{ cfgId: number }>()
|
||||
const isMobile = useIsMobile()
|
||||
|
||||
const service = ref('compute')
|
||||
const name = ref('')
|
||||
@@ -90,6 +93,10 @@ const pagination = reactive({
|
||||
pageSize: 10,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50],
|
||||
// 移动端页码槽位放不下会被裁切,切 simple 形态(‹ n/总页 ›)
|
||||
get simple() {
|
||||
return isMobile.value
|
||||
},
|
||||
onUpdatePage: (p: number) => {
|
||||
pagination.page = p
|
||||
},
|
||||
@@ -184,59 +191,59 @@ const columns = computed(() => (withAvail.value ? [...baseColumns, ...availColum
|
||||
|
||||
<template>
|
||||
<div class="panel">
|
||||
<div class="flex flex-wrap items-center gap-2 px-4.5 py-3">
|
||||
<div class="flex flex-col gap-2.5 px-4.5 py-3">
|
||||
<div class="text-sm font-semibold">服务配额</div>
|
||||
<div class="flex-1" />
|
||||
<NInputGroup class="!w-60 max-md:!w-full">
|
||||
<NInputGroupLabel size="small">区域</NInputGroupLabel>
|
||||
<NSelect
|
||||
v-model:value="region"
|
||||
size="small"
|
||||
filterable
|
||||
:options="regionOptions"
|
||||
:loading="regions.loading.value"
|
||||
:consistent-menu-width="false"
|
||||
/>
|
||||
</NInputGroup>
|
||||
<NInputGroup class="!w-72 max-lg:!w-60 max-md:!w-full">
|
||||
<NInputGroupLabel size="small">服务</NInputGroupLabel>
|
||||
<NSelect
|
||||
v-model:value="service"
|
||||
size="small"
|
||||
filterable
|
||||
:options="serviceOptions"
|
||||
:loading="services.loading.value"
|
||||
/>
|
||||
</NInputGroup>
|
||||
<NInputGroup class="!w-52 max-lg:!w-44 max-md:!w-full">
|
||||
<NInputGroupLabel size="small">作用域</NInputGroupLabel>
|
||||
<NSelect v-model:value="scopeType" size="small" :options="scopeOptions" />
|
||||
</NInputGroup>
|
||||
<NInputGroup class="!w-56 max-lg:!w-44 max-md:!w-full">
|
||||
<NInputGroupLabel size="small">配额名</NInputGroupLabel>
|
||||
<NInput
|
||||
v-model:value="name"
|
||||
size="small"
|
||||
placeholder="如 core-count"
|
||||
clearable
|
||||
@keyup.enter="limits.run()"
|
||||
@clear="limits.run()"
|
||||
/>
|
||||
</NInputGroup>
|
||||
<NCheckbox v-model:checked="hideZero" size="small">忽略 0 配额</NCheckbox>
|
||||
<NCheckbox v-model:checked="withAvail" size="small">查用量</NCheckbox>
|
||||
<FilterBar>
|
||||
<NInputGroup class="!w-60">
|
||||
<NInputGroupLabel size="small">区域</NInputGroupLabel>
|
||||
<NSelect
|
||||
v-model:value="region"
|
||||
size="small"
|
||||
filterable
|
||||
:options="regionOptions"
|
||||
:loading="regions.loading.value"
|
||||
:consistent-menu-width="false"
|
||||
/>
|
||||
</NInputGroup>
|
||||
<NInputGroup class="!w-72 max-lg:!w-60">
|
||||
<NInputGroupLabel size="small">服务</NInputGroupLabel>
|
||||
<NSelect
|
||||
v-model:value="service"
|
||||
size="small"
|
||||
filterable
|
||||
:options="serviceOptions"
|
||||
:loading="services.loading.value"
|
||||
/>
|
||||
</NInputGroup>
|
||||
<NInputGroup class="!w-56 max-lg:!w-48">
|
||||
<NInputGroupLabel size="small">作用域</NInputGroupLabel>
|
||||
<NSelect v-model:value="scopeType" size="small" :options="scopeOptions" />
|
||||
</NInputGroup>
|
||||
<NInputGroup class="!w-56 max-lg:!w-48">
|
||||
<NInputGroupLabel size="small">配额名</NInputGroupLabel>
|
||||
<NInput
|
||||
v-model:value="name"
|
||||
size="small"
|
||||
placeholder="如 core-count"
|
||||
clearable
|
||||
@keyup.enter="limits.run()"
|
||||
@clear="limits.run()"
|
||||
/>
|
||||
</NInputGroup>
|
||||
<NCheckbox v-model:checked="hideZero" size="small">忽略 0 配额</NCheckbox>
|
||||
<NCheckbox v-model:checked="withAvail" size="small">查用量</NCheckbox>
|
||||
</FilterBar>
|
||||
</div>
|
||||
<NDataTable
|
||||
:columns="columns"
|
||||
:data="rows"
|
||||
:loading="limits.loading.value"
|
||||
:pagination="pagination"
|
||||
:scroll-x="withAvail ? 760 : 380"
|
||||
:scroll-x="withAvail ? 980 : 560"
|
||||
:row-key="(r: LimitItem) => r.name + (r.availabilityDomain ?? '')"
|
||||
/>
|
||||
<FootNote>
|
||||
勾选「查用量」后单次限 100 条,超出请用配额名 / 作用域过滤收窄,配合「忽略 0
|
||||
配额」(服务端过滤)可大幅释放名额;不支持用量查询的配额自动跳过并显示「无用量数据」
|
||||
「查用量」单次限 100 条,用筛选与「忽略 0 配额」收窄;不支持的配额显示「无用量数据」
|
||||
</FootNote>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -202,7 +202,7 @@ function subLabel(d: SubscriptionDetail): string {
|
||||
</div>
|
||||
|
||||
<div v-for="d in subs.data.value" v-else :key="d.id" class="panel">
|
||||
<div class="flex items-center justify-between border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2 border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="text-sm font-semibold">云订阅</div>
|
||||
<div class="text-[12.5px] text-ink-3">{{ subLabel(d) }} · 账户类别数据来源</div>
|
||||
</div>
|
||||
@@ -258,7 +258,7 @@ function subLabel(d: SubscriptionDetail): string {
|
||||
</div>
|
||||
|
||||
<div class="panel min-w-0">
|
||||
<div class="flex items-center justify-between border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2 border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="text-sm font-semibold">区域订阅</div>
|
||||
<div class="text-[12.5px] text-ink-3">
|
||||
已订阅 {{ subscribedCount }} ·
|
||||
|
||||
@@ -158,7 +158,7 @@ const columns: DataTableColumns<IamUser> = [
|
||||
|
||||
<template>
|
||||
<div class="panel">
|
||||
<div class="flex items-center justify-between border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2 border-b border-line-soft px-4.5 py-3.5">
|
||||
<div class="text-sm font-semibold">IAM 用户</div>
|
||||
<NButton size="small" type="primary" @click="openCreate">添加用户</NButton>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user