fix(public): keep original GitHub links visible

(cherry picked from commit 7c93552697c9c35b77df35e117900ff8e9b62994)
This commit is contained in:
fawney19
2026-05-16 09:17:37 +08:00
parent f72ab383c9
commit b6d74249a4
4 changed files with 113 additions and 4 deletions

View File

@@ -0,0 +1,32 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
const apiClientMocks = vi.hoisted(() => ({
get: vi.fn(),
}))
vi.mock('@/api/client', () => ({
default: apiClientMocks,
}))
describe('useSiteInfo', () => {
beforeEach(() => {
vi.resetModules()
apiClientMocks.get.mockReset()
})
it('loads public site info', async () => {
apiClientMocks.get.mockResolvedValue({
data: {
site_name: 'Custom Aether',
site_subtitle: 'Gateway',
},
})
const { useSiteInfo } = await import('../useSiteInfo')
const { siteName, siteSubtitle, refreshSiteInfo } = useSiteInfo()
await refreshSiteInfo()
expect(siteName.value).toBe('Custom Aether')
expect(siteSubtitle.value).toBe('Gateway')
})
})

View File

@@ -0,0 +1,71 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { createApp, defineComponent, h, nextTick, type App } from 'vue'
import SiteInfoSection from '../SiteInfoSection.vue'
vi.mock('@/components/layout', async () => {
const { defineComponent, h } = await import('vue')
return {
CardSection: defineComponent({
name: 'CardSectionStub',
props: {
title: String,
description: String,
},
setup(props, { slots }) {
return () => h('section', [
h('h2', props.title),
h('p', props.description),
slots.actions?.(),
slots.default?.(),
])
},
}),
}
})
vi.mock('@/components/ui/button.vue', () => ({
default: defineComponent({
name: 'ButtonStub',
setup(_, { slots }) {
return () => h('button', slots.default?.())
},
}),
}))
const mountedApps: Array<{ app: App, root: HTMLElement }> = []
function mountSection() {
const root = document.createElement('div')
document.body.appendChild(root)
const app = createApp(SiteInfoSection, {
siteName: 'Aether',
siteSubtitle: 'AI Gateway',
loading: false,
hasChanges: true,
onSave: vi.fn(),
'onUpdate:siteName': vi.fn(),
'onUpdate:siteSubtitle': vi.fn(),
})
app.mount(root)
mountedApps.push({ app, root })
return { root }
}
afterEach(() => {
for (const { app, root } of mountedApps.splice(0)) {
app.unmount()
root.remove()
}
document.body.innerHTML = ''
})
describe('SiteInfoSection', () => {
it('renders site name and subtitle fields', async () => {
const { root } = mountSection()
await nextTick()
expect(root.textContent).toContain('站点名称')
expect(root.textContent).toContain('站点副标题')
})
})

View File

@@ -789,7 +789,8 @@
</template>
<script setup lang="ts">
import { ref, onMounted, computed, onBeforeUnmount, nextTick, watch } from 'vue'
import { ref, onMounted, computed, onBeforeUnmount, nextTick, watch, markRaw } from 'vue'
import type { Component } from 'vue'
import { useAuthStore } from '@/stores/auth'
import { dashboardApi, type DashboardStat, type DailyStat, type ProviderSummary } from '@/api/dashboard'
import { getDateRangeFromPeriod } from '@/features/usage/composables'
@@ -838,6 +839,10 @@ import type { ChartData, ChartOptions, ChartDataset, TooltipItem } from 'chart.j
const authStore = useAuthStore()
type DashboardStatCard = Omit<DashboardStat, 'icon'> & {
icon: Component
}
const statsPanelRef = ref<HTMLElement | null>(null)
const announcementsHeight = ref<number | null>(null)
const announcementsTimelineRef = ref<HTMLElement | null>(null)
@@ -941,7 +946,7 @@ const getStatIconColor = (_index: number): string => {
}
// 统计数据
const stats = ref<DashboardStat[]>([])
const stats = ref<DashboardStatCard[]>([])
const todayStats = ref<{
requests: number
tokens: number
@@ -1006,7 +1011,7 @@ const loadingAnnouncements = ref(false)
const selectedAnnouncement = ref<Announcement | null>(null)
const detailDialogOpen = ref(false)
const iconMap: Record<string, unknown> = {
const iconMap: Record<string, Component> = {
Users, Activity, TrendingUp, DollarSign, Key, Hash, Zap, Database
}
@@ -1328,7 +1333,7 @@ async function loadDashboardData() {
})
stats.value = statsData.stats.map(stat => ({
...stat,
icon: iconMap[stat.icon] || Activity
icon: markRaw(iconMap[stat.icon] || Activity)
}))
if (statsData.today) todayStats.value = statsData.today
if (isAdmin.value) {