2026-04-03 00:39:15 +08:00
|
|
|
|
<script setup lang="ts">
|
2026-05-01 20:39:09 +08:00
|
|
|
|
import { ReloadOutlined, SearchOutlined } from '@ant-design/icons-vue'
|
|
|
|
|
|
import type { DesktopAccountInfo, MediaPlatform } from '@geo/shared-types'
|
|
|
|
|
|
import { useQuery } from '@tanstack/vue-query'
|
|
|
|
|
|
import { computed, ref } from 'vue'
|
|
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
|
|
|
|
|
|
|
|
import { mediaApi, resolveApiURL, tenantAccountsApi } from '@/lib/api'
|
|
|
|
|
|
import { resolveAccountCheckedAt, resolveAccountHealth } from '@/lib/desktop-account-runtime'
|
|
|
|
|
|
import { formatDateTime } from '@/lib/display'
|
|
|
|
|
|
import {
|
|
|
|
|
|
getPublishPlatformMeta,
|
|
|
|
|
|
isMediaPublishAccount,
|
|
|
|
|
|
normalizePublishPlatformId,
|
|
|
|
|
|
} from '@/lib/publish-platforms'
|
|
|
|
|
|
|
|
|
|
|
|
type AccountHealthFilter = 'all' | 'live' | 'captcha' | 'risk' | 'expired'
|
|
|
|
|
|
type PublishState = 'immediate' | 'queued' | 'unavailable'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
|
|
|
|
|
|
interface MediaAccountCard {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
id: string
|
|
|
|
|
|
platform: string
|
|
|
|
|
|
platformLabel: string
|
|
|
|
|
|
platformShortName: string
|
|
|
|
|
|
platformAccent: string
|
|
|
|
|
|
platformLogoUrl: string | null
|
|
|
|
|
|
displayName: string
|
|
|
|
|
|
platformUid: string
|
|
|
|
|
|
avatarUrl: string | null
|
|
|
|
|
|
health: DesktopAccountInfo['health']
|
|
|
|
|
|
verifiedAt: string | null
|
|
|
|
|
|
clientId: string | null
|
|
|
|
|
|
clientOnline: boolean | null
|
|
|
|
|
|
clientDeviceName: string | null
|
|
|
|
|
|
clientLastSeenAt: string | null
|
|
|
|
|
|
publishState: PublishState
|
|
|
|
|
|
publishHint: string
|
|
|
|
|
|
deleteRequestedAt: string | null
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const platformLogoCatalog: Record<string, string> = {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
toutiaohao: '/logos/logo_toutiao.png',
|
|
|
|
|
|
baijiahao: '/logos/logo_baijiahao.png',
|
|
|
|
|
|
sohuhao: '/logos/logo_souhu.png',
|
|
|
|
|
|
qiehao: '/logos/logo_qiehao.png',
|
|
|
|
|
|
zhihu: '/logos/logo_zhihu.png',
|
|
|
|
|
|
wangyihao: '/logos/logo_wangyihao.png',
|
|
|
|
|
|
jianshu: '/logos/logo_jianshu.svg',
|
|
|
|
|
|
bilibili: '/logos/logo_bilibili.png',
|
|
|
|
|
|
juejin: '/logos/logo_juejin.png',
|
|
|
|
|
|
smzdm: '/logos/logo_smzdm.svg',
|
|
|
|
|
|
weixin_gzh: '/logos/logo_weixin_gzh.svg',
|
|
|
|
|
|
zol: '/logos/logo_zol.png',
|
|
|
|
|
|
dongchedi: '/logos/logo_dongchedi.svg',
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
|
|
|
|
|
const searchQuery = ref('')
|
|
|
|
|
|
const selectedHealth = ref<AccountHealthFilter>('all')
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
|
|
|
|
|
const platformsQuery = useQuery({
|
2026-05-01 20:39:09 +08:00
|
|
|
|
queryKey: ['media', 'platforms', 'media-view'],
|
2026-04-03 00:39:15 +08:00
|
|
|
|
queryFn: () => mediaApi.platforms(),
|
2026-05-01 20:39:09 +08:00
|
|
|
|
})
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
|
|
|
|
|
const accountsQuery = useQuery({
|
2026-05-01 20:39:09 +08:00
|
|
|
|
queryKey: ['tenant', 'desktop-accounts', 'media-view'],
|
2026-04-20 09:52:48 +08:00
|
|
|
|
queryFn: () => tenantAccountsApi.list(),
|
2026-05-13 19:36:24 +08:00
|
|
|
|
staleTime: 0,
|
|
|
|
|
|
refetchInterval: 5_000,
|
|
|
|
|
|
refetchOnWindowFocus: true,
|
2026-05-01 20:39:09 +08:00
|
|
|
|
})
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
|
const loading = computed(() => platformsQuery.isPending.value || accountsQuery.isPending.value)
|
|
|
|
|
|
const refreshing = computed(() => platformsQuery.isFetching.value || accountsQuery.isFetching.value)
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
const platformMap = computed(() => {
|
|
|
|
|
|
return new Map(
|
|
|
|
|
|
(platformsQuery.data.value ?? []).map((platform: MediaPlatform) => [
|
|
|
|
|
|
normalizePublishPlatformId(platform.platform_id),
|
|
|
|
|
|
platform,
|
|
|
|
|
|
]),
|
2026-05-01 20:39:09 +08:00
|
|
|
|
)
|
|
|
|
|
|
})
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
const mediaAccounts = computed<MediaAccountCard[]>(() => {
|
|
|
|
|
|
return [...(accountsQuery.data.value ?? [])]
|
2026-05-01 16:01:40 +08:00
|
|
|
|
.filter(isMediaPublishAccount)
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.map((account) => {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
const platformId = normalizePublishPlatformId(account.platform)
|
|
|
|
|
|
const platform = platformMap.value.get(platformId)
|
|
|
|
|
|
const fallback = getPublishPlatformMeta(platformId)
|
|
|
|
|
|
const publishState = resolvePublishState(account)
|
|
|
|
|
|
const health = resolveAccountHealth(account)
|
2026-04-20 09:52:48 +08:00
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: account.id,
|
|
|
|
|
|
platform: platformId,
|
|
|
|
|
|
platformLabel: platform?.name || fallback.name,
|
|
|
|
|
|
platformShortName: platform?.short_name || fallback.shortName,
|
|
|
|
|
|
platformAccent: platform?.accent_color || fallback.accent,
|
|
|
|
|
|
platformLogoUrl: resolvePlatformLogoUrl(platformId, platform?.logo_url),
|
|
|
|
|
|
displayName: account.display_name,
|
|
|
|
|
|
platformUid: normalizePlatformUid(account.platform_uid),
|
|
|
|
|
|
avatarUrl: resolveApiURL(account.avatar_url),
|
2026-04-27 21:34:05 +08:00
|
|
|
|
health,
|
|
|
|
|
|
verifiedAt: resolveAccountCheckedAt(account),
|
2026-04-20 09:52:48 +08:00
|
|
|
|
clientId: account.client_id,
|
|
|
|
|
|
clientOnline: account.client_online,
|
|
|
|
|
|
clientDeviceName: account.client_device_name,
|
|
|
|
|
|
clientLastSeenAt: account.client_last_seen_at,
|
|
|
|
|
|
publishState,
|
|
|
|
|
|
publishHint: resolvePublishHint(account, publishState),
|
|
|
|
|
|
deleteRequestedAt: account.delete_requested_at,
|
2026-05-01 20:39:09 +08:00
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
|
})
|
|
|
|
|
|
.sort((left, right) => {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
const publishGap = publishRank(left.publishState) - publishRank(right.publishState)
|
2026-04-20 09:52:48 +08:00
|
|
|
|
if (publishGap !== 0) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return publishGap
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
|
const leftAt = Date.parse(left.verifiedAt ?? '') || 0
|
|
|
|
|
|
const rightAt = Date.parse(right.verifiedAt ?? '') || 0
|
|
|
|
|
|
return rightAt - leftAt
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
const overview = computed(() => ({
|
|
|
|
|
|
total: mediaAccounts.value.length,
|
2026-05-01 20:39:09 +08:00
|
|
|
|
live: mediaAccounts.value.filter((item) => item.health === 'live').length,
|
|
|
|
|
|
immediate: mediaAccounts.value.filter((item) => item.publishState === 'immediate').length,
|
|
|
|
|
|
queued: mediaAccounts.value.filter((item) => item.publishState === 'queued').length,
|
|
|
|
|
|
attention: mediaAccounts.value.filter((item) => item.publishState === 'unavailable').length,
|
|
|
|
|
|
}))
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
const filteredAccounts = computed(() => {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
const keyword = searchQuery.value.trim().toLowerCase()
|
2026-04-20 09:52:48 +08:00
|
|
|
|
|
|
|
|
|
|
return mediaAccounts.value.filter((account) => {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
if (selectedHealth.value !== 'all' && account.health !== selectedHealth.value) {
|
|
|
|
|
|
return false
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
|
|
|
|
|
|
|
if (!keyword) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return true
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
account.displayName,
|
|
|
|
|
|
account.platformLabel,
|
|
|
|
|
|
account.platformUid,
|
2026-05-01 20:39:09 +08:00
|
|
|
|
account.clientDeviceName ?? '',
|
|
|
|
|
|
]
|
|
|
|
|
|
.join(' ')
|
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
|
.includes(keyword)
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function resolvePublishState(account: DesktopAccountInfo): PublishState {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
if (resolveAccountHealth(account) !== 'live') {
|
|
|
|
|
|
return 'unavailable'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (!account.client_id) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return 'unavailable'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return account.client_online === true ? 'immediate' : 'queued'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function resolvePublishHint(account: DesktopAccountInfo, publishState: PublishState): string {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
if (publishState === 'immediate') {
|
|
|
|
|
|
return ''
|
2026-04-25 18:09:12 +08:00
|
|
|
|
}
|
2026-05-01 20:39:09 +08:00
|
|
|
|
if (publishState === 'queued') {
|
|
|
|
|
|
return '客户端当前离线,任务会先进入发布队列,等桌面端上线后自动继续。'
|
2026-04-25 18:09:12 +08:00
|
|
|
|
}
|
2026-05-01 20:39:09 +08:00
|
|
|
|
if (resolveAccountHealth(account) !== 'live') {
|
|
|
|
|
|
return '授权状态异常,先在桌面端重新校验后再发布。'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return '还没有绑定桌面客户端,当前无法加入发布队列。'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function normalizePlatformUid(value?: string | null): string {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
let normalized = String(value ?? '').trim()
|
|
|
|
|
|
while (normalized.toLowerCase().startsWith('platform:')) {
|
|
|
|
|
|
normalized = normalized.slice('platform:'.length).trim()
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return normalized || '--'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function resolvePlatformLogoUrl(platformId: string, remoteLogoUrl?: string | null): string | null {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
const localLogoUrl = platformLogoCatalog[platformId]
|
2026-04-20 09:52:48 +08:00
|
|
|
|
if (localLogoUrl) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return localLogoUrl
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
|
const resolvedRemoteLogoUrl = resolveApiURL(remoteLogoUrl)
|
|
|
|
|
|
return resolvedRemoteLogoUrl || null
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function accountInitial(account: MediaAccountCard): string {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
const name = account.displayName.trim()
|
|
|
|
|
|
return name ? name.slice(0, 1).toUpperCase() : account.platformShortName
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function publishRank(state: PublishState): number {
|
|
|
|
|
|
switch (state) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
case 'immediate':
|
|
|
|
|
|
return 0
|
|
|
|
|
|
case 'queued':
|
|
|
|
|
|
return 1
|
2026-04-20 09:52:48 +08:00
|
|
|
|
default:
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return 2
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
|
function healthLabel(health: DesktopAccountInfo['health']): string {
|
2026-04-20 09:52:48 +08:00
|
|
|
|
switch (health) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
case 'live':
|
|
|
|
|
|
return '授权正常'
|
|
|
|
|
|
case 'captcha':
|
|
|
|
|
|
return '待处理'
|
|
|
|
|
|
case 'risk':
|
|
|
|
|
|
return '风险观察'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
default:
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return '授权异常'
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
|
function healthColor(health: DesktopAccountInfo['health']): string {
|
2026-04-20 09:52:48 +08:00
|
|
|
|
switch (health) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
case 'live':
|
|
|
|
|
|
return 'green'
|
|
|
|
|
|
case 'captcha':
|
|
|
|
|
|
return 'orange'
|
|
|
|
|
|
case 'risk':
|
|
|
|
|
|
return 'gold'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
default:
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return 'red'
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function clientStatusLabel(account: MediaAccountCard): string {
|
|
|
|
|
|
if (!account.clientId) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return '未绑定'
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return account.clientOnline ? '在线' : '离线'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function clientStatusColor(account: MediaAccountCard): string {
|
|
|
|
|
|
if (!account.clientId) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return 'default'
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return account.clientOnline ? 'green' : 'gold'
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function clientStatusText(account: MediaAccountCard): string {
|
|
|
|
|
|
if (!account.clientId) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return '未绑定桌面客户端'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
|
const device = account.clientDeviceName?.trim()
|
|
|
|
|
|
return device ? `${clientStatusLabel(account)} · ${device}` : clientStatusLabel(account)
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function publishStateLabel(state: PublishState): string {
|
|
|
|
|
|
switch (state) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
case 'immediate':
|
|
|
|
|
|
return '可立即发布'
|
|
|
|
|
|
case 'queued':
|
|
|
|
|
|
return '离线排队'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
default:
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return '不可发布'
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
function publishStateColor(state: PublishState): string {
|
|
|
|
|
|
switch (state) {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
case 'immediate':
|
|
|
|
|
|
return 'green'
|
|
|
|
|
|
case 'queued':
|
|
|
|
|
|
return 'gold'
|
2026-04-20 09:52:48 +08:00
|
|
|
|
default:
|
2026-05-01 20:39:09 +08:00
|
|
|
|
return 'red'
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
async function refreshAll(): Promise<void> {
|
2026-05-01 20:39:09 +08:00
|
|
|
|
await Promise.all([platformsQuery.refetch(), accountsQuery.refetch()])
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="media-view">
|
|
|
|
|
|
<section class="media-view__top-card">
|
|
|
|
|
|
<div class="media-view__header">
|
|
|
|
|
|
<div class="media-view__header-title">
|
2026-05-01 20:39:09 +08:00
|
|
|
|
<h2>{{ t('route.media.title') }}</h2>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
这里展示客户端里当前用户已绑定的媒体账号,以及展示它们是否能立即发布、离线排队或需要处理。
|
|
|
|
|
|
</p>
|
2026-04-03 00:39:15 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="media-view__header-actions">
|
2026-04-20 09:52:48 +08:00
|
|
|
|
<a-button :loading="refreshing" @click="refreshAll">
|
2026-04-03 00:39:15 +08:00
|
|
|
|
<template #icon><ReloadOutlined /></template>
|
2026-04-20 09:52:48 +08:00
|
|
|
|
刷新状态
|
2026-04-03 00:39:15 +08:00
|
|
|
|
</a-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-04-20 09:52:48 +08:00
|
|
|
|
|
|
|
|
|
|
<div class="overview-strip border-t">
|
|
|
|
|
|
<div class="overview-chip">
|
|
|
|
|
|
<span>账号总数</span>
|
|
|
|
|
|
<strong>{{ overview.total }}</strong>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="overview-chip">
|
|
|
|
|
|
<span>可立即发布</span>
|
|
|
|
|
|
<strong>{{ overview.immediate }}</strong>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="overview-chip">
|
|
|
|
|
|
<span>离线排队</span>
|
|
|
|
|
|
<strong>{{ overview.queued }}</strong>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="overview-chip">
|
|
|
|
|
|
<span>需处理</span>
|
|
|
|
|
|
<strong>{{ overview.attention }}</strong>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-04-03 00:39:15 +08:00
|
|
|
|
</section>
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
<section class="panel media-list-panel">
|
|
|
|
|
|
<div class="media-list-toolbar">
|
|
|
|
|
|
<a-input
|
|
|
|
|
|
v-model:value="searchQuery"
|
|
|
|
|
|
placeholder="搜索昵称、UID、平台、客户端"
|
|
|
|
|
|
class="media-search"
|
|
|
|
|
|
allow-clear
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #prefix>
|
|
|
|
|
|
<SearchOutlined style="color: #8c8c8c" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-input>
|
|
|
|
|
|
|
|
|
|
|
|
<a-select v-model:value="selectedHealth" style="width: 160px">
|
|
|
|
|
|
<a-select-option value="all">全部状态</a-select-option>
|
|
|
|
|
|
<a-select-option value="live">授权正常</a-select-option>
|
|
|
|
|
|
<a-select-option value="captcha">待处理</a-select-option>
|
|
|
|
|
|
<a-select-option value="risk">风险观察</a-select-option>
|
|
|
|
|
|
<a-select-option value="expired">授权异常</a-select-option>
|
|
|
|
|
|
</a-select>
|
2026-04-03 00:39:15 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
<div class="media-list-header">
|
|
|
|
|
|
<h3 class="panel-title">已绑定媒体账号</h3>
|
2026-04-03 00:39:15 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
<a-spin :spinning="loading">
|
|
|
|
|
|
<a-empty
|
|
|
|
|
|
v-if="!filteredAccounts.length"
|
|
|
|
|
|
description="当前还没有可展示的媒体账号,请先在桌面端绑定。"
|
|
|
|
|
|
/>
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
<section v-else class="media-grid">
|
2026-05-01 20:39:09 +08:00
|
|
|
|
<article v-for="account in filteredAccounts" :key="account.id" class="media-card">
|
2026-04-20 09:52:48 +08:00
|
|
|
|
<div class="media-card__head">
|
|
|
|
|
|
<div class="media-card__identity">
|
|
|
|
|
|
<span class="media-card__avatar" :style="{ background: account.platformAccent }">
|
2026-05-01 20:39:09 +08:00
|
|
|
|
<img
|
|
|
|
|
|
v-if="account.avatarUrl"
|
|
|
|
|
|
:src="account.avatarUrl"
|
|
|
|
|
|
:alt="account.displayName"
|
|
|
|
|
|
referrerpolicy="no-referrer"
|
|
|
|
|
|
/>
|
2026-04-20 09:52:48 +08:00
|
|
|
|
<span v-else>{{ accountInitial(account) }}</span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="media-card__identity-copy">
|
2026-05-13 19:36:24 +08:00
|
|
|
|
<h3>
|
|
|
|
|
|
{{ account.displayName }}
|
|
|
|
|
|
<img
|
|
|
|
|
|
v-if="account.platformLogoUrl"
|
|
|
|
|
|
:src="account.platformLogoUrl"
|
|
|
|
|
|
:alt="account.platformLabel"
|
|
|
|
|
|
class="media-card__inline-platform-icon"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<span v-else class="media-card__inline-platform-text" :style="{ color: account.platformAccent }">{{ account.platformShortName }}</span>
|
|
|
|
|
|
</h3>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
<span class="media-card__platform-name">{{ account.platformLabel }}</span>
|
|
|
|
|
|
<span class="media-card__uid-divider">|</span>
|
|
|
|
|
|
<span class="media-card__uid">UID: {{ account.platformUid }}</span>
|
|
|
|
|
|
</p>
|
2026-04-20 09:52:48 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-04-03 00:39:15 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-05-13 19:36:24 +08:00
|
|
|
|
<div class="media-card__tags">
|
|
|
|
|
|
<a-tag :color="healthColor(account.health)">
|
|
|
|
|
|
{{ healthLabel(account.health) }}
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
<a-tag :color="publishStateColor(account.publishState)">
|
|
|
|
|
|
{{ publishStateLabel(account.publishState) }}
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
<a-tooltip :title="clientStatusText(account)" placement="top">
|
|
|
|
|
|
<a-tag :color="clientStatusColor(account)" class="client-status-tag">
|
|
|
|
|
|
{{ clientStatusText(account) }}
|
2026-05-01 20:39:09 +08:00
|
|
|
|
</a-tag>
|
2026-05-13 19:36:24 +08:00
|
|
|
|
</a-tooltip>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="media-card__meta-box">
|
|
|
|
|
|
<div class="meta-box-item">
|
2026-04-20 09:52:48 +08:00
|
|
|
|
<span class="meta-label">最近校验</span>
|
2026-05-13 19:36:24 +08:00
|
|
|
|
<span class="meta-value">{{ account.verifiedAt ? formatDateTime(account.verifiedAt) : '--' }}</span>
|
2026-04-20 09:52:48 +08:00
|
|
|
|
</div>
|
2026-05-13 19:36:24 +08:00
|
|
|
|
<div class="meta-box-item">
|
2026-04-20 09:52:48 +08:00
|
|
|
|
<span class="meta-label">最近心跳</span>
|
2026-05-13 19:36:24 +08:00
|
|
|
|
<span class="meta-value">{{ account.clientLastSeenAt ? formatDateTime(account.clientLastSeenAt) : '--' }}</span>
|
2026-04-20 09:52:48 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-25 17:39:02 +08:00
|
|
|
|
<div v-if="account.deleteRequestedAt || account.publishHint" class="media-card__footer">
|
2026-04-20 09:52:48 +08:00
|
|
|
|
<span v-if="account.deleteRequestedAt" class="footer-badge footer-badge--warning">
|
|
|
|
|
|
已申请解绑
|
|
|
|
|
|
</span>
|
2026-04-25 17:39:02 +08:00
|
|
|
|
<span v-else-if="account.publishHint" class="footer-badge">
|
2026-04-20 09:52:48 +08:00
|
|
|
|
{{ account.publishHint }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</article>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
</a-spin>
|
2026-04-03 00:39:15 +08:00
|
|
|
|
</section>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.media-view {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-view__top-card {
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
border: 1px solid #e6edf5;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-view__header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
padding: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-view__header-title h2 {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #1a1a1a;
|
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-view__header-title p {
|
2026-04-20 09:52:48 +08:00
|
|
|
|
margin: 6px 0 0;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #8c8c8c;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
line-height: 1.7;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-view__header-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.overview-strip {
|
2026-04-03 00:39:15 +08:00
|
|
|
|
display: grid;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.overview-chip {
|
|
|
|
|
|
padding: 18px 24px;
|
|
|
|
|
|
border-top: 1px solid #eef2f7;
|
|
|
|
|
|
border-right: 1px solid #eef2f7;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.overview-chip:last-child {
|
|
|
|
|
|
border-right: none;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.overview-chip span {
|
2026-04-03 00:39:15 +08:00
|
|
|
|
display: block;
|
|
|
|
|
|
color: #8c8c8c;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.overview-chip strong {
|
2026-04-03 00:39:15 +08:00
|
|
|
|
display: block;
|
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
|
color: #1a1a1a;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
font-size: 24px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.panel {
|
2026-04-03 00:39:15 +08:00
|
|
|
|
background: #fff;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
padding: 24px;
|
|
|
|
|
|
border: 1px solid #e6edf5;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.media-list-panel {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 20px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-list-toolbar {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
gap: 16px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-search {
|
|
|
|
|
|
width: 320px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.media-list-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 6px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.panel-title {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 16px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: #1a1a1a;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.panel-desc {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
color: #8c8c8c;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
line-height: 1.7;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
|
|
|
|
gap: 18px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-card {
|
2026-05-13 19:36:24 +08:00
|
|
|
|
border: 1px solid #f0f0f0;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
background: #ffffff;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2026-05-13 19:36:24 +08:00
|
|
|
|
gap: 16px;
|
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-card:hover {
|
|
|
|
|
|
box-shadow: 0 12px 28px -6px rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
border-color: transparent;
|
|
|
|
|
|
transform: translateY(-2px);
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-card__head {
|
|
|
|
|
|
display: flex;
|
2026-05-13 19:36:24 +08:00
|
|
|
|
align-items: center;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-card__identity {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-05-13 19:36:24 +08:00
|
|
|
|
gap: 14px;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
min-width: 0;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.media-card__avatar {
|
2026-05-13 19:36:24 +08:00
|
|
|
|
width: 48px;
|
|
|
|
|
|
height: 48px;
|
|
|
|
|
|
border-radius: 50%;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-size: 18px;
|
2026-05-13 19:36:24 +08:00
|
|
|
|
font-weight: 600;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
flex-shrink: 0;
|
2026-05-13 19:36:24 +08:00
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.media-card__avatar img {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
object-fit: cover;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.media-card__identity-copy {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-card__identity-copy h3 {
|
2026-04-03 00:39:15 +08:00
|
|
|
|
margin: 0;
|
2026-05-13 19:36:24 +08:00
|
|
|
|
color: #1e293b;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-card__inline-platform-icon {
|
|
|
|
|
|
width: 16px;
|
|
|
|
|
|
height: 16px;
|
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-card__inline-platform-text {
|
|
|
|
|
|
font-size: 12px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
font-weight: 700;
|
2026-05-13 19:36:24 +08:00
|
|
|
|
background: #f8fafc;
|
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
|
border-radius: 4px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.media-card__identity-copy p {
|
|
|
|
|
|
margin: 4px 0 0;
|
2026-05-13 19:36:24 +08:00
|
|
|
|
color: #64748b;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-card__platform-name {
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-card__uid-divider {
|
|
|
|
|
|
color: #cbd5e1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.media-card__uid {
|
|
|
|
|
|
color: #94a3b8;
|
|
|
|
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
font-size: 12px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 19:36:24 +08:00
|
|
|
|
.media-card__tags {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 8px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
|
|
2026-05-13 19:36:24 +08:00
|
|
|
|
.media-card__tags :deep(.ant-tag) {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
padding: 2px 8px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 19:36:24 +08:00
|
|
|
|
.media-card__meta-box {
|
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 12px 16px;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2026-05-13 19:36:24 +08:00
|
|
|
|
gap: 8px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 19:36:24 +08:00
|
|
|
|
.meta-box-item {
|
2026-04-20 09:52:48 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
2026-05-13 19:36:24 +08:00
|
|
|
|
font-size: 12px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.meta-label {
|
2026-05-13 19:36:24 +08:00
|
|
|
|
color: #64748b;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.meta-value {
|
2026-05-13 19:36:24 +08:00
|
|
|
|
color: #334155;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
font-weight: 500;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 12:00:10 +08:00
|
|
|
|
.client-status-tag {
|
2026-05-13 19:36:24 +08:00
|
|
|
|
max-width: 140px;
|
2026-04-20 12:00:10 +08:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 00:39:15 +08:00
|
|
|
|
.media-card__footer {
|
2026-04-20 09:52:48 +08:00
|
|
|
|
padding-top: 4px;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.footer-badge {
|
2026-04-03 00:39:15 +08:00
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
min-height: 28px;
|
|
|
|
|
|
padding: 0 10px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: #f3f6fb;
|
|
|
|
|
|
color: #516074;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
font-size: 12px;
|
2026-04-20 09:52:48 +08:00
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
line-height: 1.6;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.footer-badge--warning {
|
|
|
|
|
|
background: #fff7e6;
|
|
|
|
|
|
color: #d48806;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
@media (max-width: 900px) {
|
|
|
|
|
|
.overview-strip {
|
|
|
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.overview-chip:nth-child(2) {
|
|
|
|
|
|
border-right: none;
|
|
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.overview-chip:nth-child(n + 3) {
|
|
|
|
|
|
border-top: 1px solid #eef2f7;
|
|
|
|
|
|
}
|
2026-04-03 00:39:15 +08:00
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.media-list-toolbar {
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: stretch;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
|
.media-search {
|
|
|
|
|
|
width: 100%;
|
2026-04-03 00:39:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|