滑动续期与多标签同步、列表报错提示、分页排序体验改进

This commit is contained in:
2026-08-11 11:44:57 +08:00
parent ec8a50c4c0
commit 343563bb79
18 changed files with 358 additions and 24 deletions
+54 -2
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { NButton, NSelect, NSpin, NTabPane, NTabs, NTooltip } from 'naive-ui'
import { computed, ref, watch } from 'vue'
import { computed, onUnmounted, ref, watch } from 'vue'
import { RouterLink, useRoute, useRouter } from 'vue-router'
import { deleteConfig, getConfig, verifyConfig } from '@/api/configs'
@@ -43,6 +43,32 @@ const heroExpanded = ref(false)
const config = useAsync(() => getConfig(cfgId.value))
function refreshAfterKeyActivation() {
void config.run()
void scope.refreshConfigs()
}
/** 新 key 激活后的 OCI 公钥全球传播窗口:窗口内显示提示横幅,到点自动消失 */
const KEY_PROPAGATION_MS = 15 * 60 * 1000
const propagationNow = ref(Date.now())
let propagationTimer = 0
const keyPropagating = computed(() => {
const at = config.data.value?.keyActivatedAt
return !!at && propagationNow.value - new Date(at).getTime() < KEY_PROPAGATION_MS
})
watch(
() => config.data.value?.keyActivatedAt,
(at) => {
window.clearTimeout(propagationTimer)
if (!at) return
const left = new Date(at).getTime() + KEY_PROPAGATION_MS - Date.now()
if (left > 0)
propagationTimer = window.setTimeout(() => (propagationNow.value = Date.now()), left + 1000)
},
{ immediate: true },
)
onUnmounted(() => window.clearTimeout(propagationTimer))
/** 失联/暂停租户各 tab 拉不到云端数据,内容区整体换占位 */
const isBlocked = computed(() =>
['dead', 'suspended'].includes(config.data.value?.aliveStatus ?? ''),
@@ -158,6 +184,26 @@ async function remove() {
</div>
</div>
<div
v-if="keyPropagating"
class="mt-3 flex items-start gap-2 rounded-lg border border-warn/30 bg-warn/10 px-3 py-2.5 text-xs leading-relaxed"
>
<svg
class="mt-0.5 h-[14px] w-[14px] flex-none text-warn"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="12" cy="12" r="10" />
<path d="M12 8v4M12 16h.01" />
</svg>
<span>
新 API Key 正在全球生效中(约需数分钟):期间部分区域可能间歇报
NotAuthenticated,属预期现象,请稍后重试
</span>
</div>
<button
v-if="isMobile"
class="mt-2 flex cursor-pointer items-center gap-1 text-xs text-ink-3"
@@ -231,7 +277,13 @@ async function remove() {
<QuotaTab v-if="tab === 'quota'" :cfg-id="cfgId" />
<!-- 域相关 tab 以 domainId 为 key:切域整组件重挂载,状态与数据全量刷新 -->
<IdpTab v-else-if="tab === 'idp'" :key="`idp-${domainId ?? ''}`" :cfg-id="cfgId" :domain-id="domainId" />
<UsersTab v-else-if="tab === 'users'" :key="`users-${domainId ?? ''}`" :cfg-id="cfgId" :domain-id="domainId" />
<UsersTab
v-else-if="tab === 'users'"
:key="`users-${domainId ?? ''}`"
:cfg-id="cfgId"
:domain-id="domainId"
@key-activated="refreshAfterKeyActivation"
/>
<CostTab v-else-if="tab === 'cost'" :cfg-id="cfgId" />
<InvoiceTab v-else-if="tab === 'invoice'" :cfg-id="cfgId" />
<SubsTab v-else-if="tab === 'subs'" :cfg-id="cfgId" :account-type="config.data.value.accountType" />