网盘搜索前增加来源筛选
This commit is contained in:
@@ -19,12 +19,15 @@ export async function POST(request: NextRequest) {
|
|||||||
|
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const { keyword } = body;
|
const { keyword } = body;
|
||||||
|
const cloudTypes = Array.isArray(body.cloud_types)
|
||||||
|
? body.cloud_types.filter(
|
||||||
|
(item: unknown): item is string =>
|
||||||
|
typeof item === 'string' && item.trim().length > 0
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
if (!keyword) {
|
if (!keyword) {
|
||||||
return NextResponse.json(
|
return NextResponse.json({ error: '关键词不能为空' }, { status: 400 });
|
||||||
{ error: '关键词不能为空' },
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从系统配置中获取 Pansou 配置
|
// 从系统配置中获取 Pansou 配置
|
||||||
@@ -37,6 +40,7 @@ export async function POST(request: NextRequest) {
|
|||||||
keyword,
|
keyword,
|
||||||
apiUrl: apiUrl ? '已配置' : '未配置',
|
apiUrl: apiUrl ? '已配置' : '未配置',
|
||||||
hasAuth: !!(username && password),
|
hasAuth: !!(username && password),
|
||||||
|
cloudTypes: cloudTypes?.length ? cloudTypes : 'all',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!apiUrl) {
|
if (!apiUrl) {
|
||||||
@@ -50,6 +54,7 @@ export async function POST(request: NextRequest) {
|
|||||||
const results = await searchPansou(apiUrl, keyword, {
|
const results = await searchPansou(apiUrl, keyword, {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
|
cloudTypes,
|
||||||
});
|
});
|
||||||
|
|
||||||
const rawBlocklist = config.SiteConfig.PansouKeywordBlocklist || '';
|
const rawBlocklist = config.SiteConfig.PansouKeywordBlocklist || '';
|
||||||
@@ -66,7 +71,9 @@ export async function POST(request: NextRequest) {
|
|||||||
let total = 0;
|
let total = 0;
|
||||||
|
|
||||||
const shouldBlock = (link: PansouLink) => {
|
const shouldBlock = (link: PansouLink) => {
|
||||||
const content = `${link.note || ''} ${link.url || ''} ${link.source || ''}`.toLowerCase();
|
const content = `${link.note || ''} ${link.url || ''} ${
|
||||||
|
link.source || ''
|
||||||
|
}`.toLowerCase();
|
||||||
return blockedKeywords.some((item) =>
|
return blockedKeywords.some((item) =>
|
||||||
content.includes(item.toLowerCase())
|
content.includes(item.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|||||||
+196
-7
@@ -21,6 +21,7 @@ import React, {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
import { getAuthInfoFromBrowserCookie } from '@/lib/auth';
|
import { getAuthInfoFromBrowserCookie } from '@/lib/auth';
|
||||||
import {
|
import {
|
||||||
@@ -37,7 +38,7 @@ import AcgSearch from '@/components/AcgSearch';
|
|||||||
import CapsuleSwitch from '@/components/CapsuleSwitch';
|
import CapsuleSwitch from '@/components/CapsuleSwitch';
|
||||||
import ImageViewer from '@/components/ImageViewer';
|
import ImageViewer from '@/components/ImageViewer';
|
||||||
import PageLayout from '@/components/PageLayout';
|
import PageLayout from '@/components/PageLayout';
|
||||||
import PansouSearch from '@/components/PansouSearch';
|
import PansouSearch, { CLOUD_TYPE_NAMES } from '@/components/PansouSearch';
|
||||||
import ProxyImage from '@/components/ProxyImage';
|
import ProxyImage from '@/components/ProxyImage';
|
||||||
import SearchResultFilter, {
|
import SearchResultFilter, {
|
||||||
SearchFilterCategory,
|
SearchFilterCategory,
|
||||||
@@ -46,6 +47,10 @@ import SearchSuggestions from '@/components/SearchSuggestions';
|
|||||||
import VideoCard, { VideoCardHandle } from '@/components/VideoCard';
|
import VideoCard, { VideoCardHandle } from '@/components/VideoCard';
|
||||||
import VirtualScrollableGrid from '@/components/VirtualScrollableGrid';
|
import VirtualScrollableGrid from '@/components/VirtualScrollableGrid';
|
||||||
|
|
||||||
|
const PANSOU_CLOUD_TYPE_OPTIONS = Object.entries(CLOUD_TYPE_NAMES).map(
|
||||||
|
([value, label]) => ({ value, label })
|
||||||
|
);
|
||||||
|
|
||||||
type SearchCachePayload = {
|
type SearchCachePayload = {
|
||||||
status: 'complete' | 'partial';
|
status: 'complete' | 'partial';
|
||||||
results: SearchResult[];
|
results: SearchResult[];
|
||||||
@@ -66,6 +71,17 @@ function SearchPageClient() {
|
|||||||
const [triggerPansouSearch, setTriggerPansouSearch] = useState(false);
|
const [triggerPansouSearch, setTriggerPansouSearch] = useState(false);
|
||||||
// ACG 搜索触发标志
|
// ACG 搜索触发标志
|
||||||
const [triggerAcgSearch, setTriggerAcgSearch] = useState(false);
|
const [triggerAcgSearch, setTriggerAcgSearch] = useState(false);
|
||||||
|
const [selectedPansouCloudTypes, setSelectedPansouCloudTypes] = useState<
|
||||||
|
string[]
|
||||||
|
>([]);
|
||||||
|
const [pansouCloudFilterOpen, setPansouCloudFilterOpen] = useState(false);
|
||||||
|
const [pansouCloudFilterPosition, setPansouCloudFilterPosition] = useState({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 0,
|
||||||
|
});
|
||||||
|
const pansouCloudFilterButtonRef = useRef<HTMLButtonElement | null>(null);
|
||||||
|
const pansouCloudFilterDropdownRef = useRef<HTMLDivElement | null>(null);
|
||||||
// 用户权限
|
// 用户权限
|
||||||
const [userRole, setUserRole] = useState<'owner' | 'admin' | 'user' | null>(
|
const [userRole, setUserRole] = useState<'owner' | 'admin' | 'user' | null>(
|
||||||
null
|
null
|
||||||
@@ -1520,6 +1536,115 @@ function SearchPageClient() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const togglePansouCloudType = (cloudType: string) => {
|
||||||
|
setSelectedPansouCloudTypes((prev) =>
|
||||||
|
prev.includes(cloudType)
|
||||||
|
? prev.filter((type) => type !== cloudType)
|
||||||
|
: [...prev, cloudType]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculatePansouCloudFilterPosition = () => {
|
||||||
|
const element = pansouCloudFilterButtonRef.current;
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
const viewportWidth = window.innerWidth;
|
||||||
|
const padding = 16;
|
||||||
|
const width = Math.min(320, viewportWidth - padding * 2);
|
||||||
|
let x = rect.left;
|
||||||
|
|
||||||
|
if (x + width > viewportWidth - padding) {
|
||||||
|
x = viewportWidth - width - padding;
|
||||||
|
}
|
||||||
|
if (x < padding) {
|
||||||
|
x = padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPansouCloudFilterPosition({ x, y: rect.bottom + 8, width });
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedPansouCloudTypeLabels = selectedPansouCloudTypes
|
||||||
|
.map((type) => CLOUD_TYPE_NAMES[type] || type)
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
const renderPansouCloudTypeFilter = () => {
|
||||||
|
const hasFilter = selectedPansouCloudTypes.length > 0;
|
||||||
|
const displayText = hasFilter
|
||||||
|
? selectedPansouCloudTypes.length === 1
|
||||||
|
? selectedPansouCloudTypeLabels[0]
|
||||||
|
: `网盘类型 · ${selectedPansouCloudTypes.length}`
|
||||||
|
: '网盘类型';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='mx-auto mt-4 flex max-w-2xl justify-end overflow-visible'>
|
||||||
|
<button
|
||||||
|
ref={pansouCloudFilterButtonRef}
|
||||||
|
type='button'
|
||||||
|
onClick={() => {
|
||||||
|
if (!pansouCloudFilterOpen) {
|
||||||
|
calculatePansouCloudFilterPosition();
|
||||||
|
}
|
||||||
|
setPansouCloudFilterOpen((prev) => !prev);
|
||||||
|
}}
|
||||||
|
className={`relative z-10 rounded-full px-3 py-1 text-xs font-medium transition-all duration-200 whitespace-nowrap focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900 ${
|
||||||
|
hasFilter
|
||||||
|
? 'cursor-pointer text-green-600 hover:text-green-700 dark:text-green-400 dark:hover:text-green-300'
|
||||||
|
: 'cursor-pointer text-gray-700 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-100'
|
||||||
|
}`}
|
||||||
|
aria-expanded={pansouCloudFilterOpen}
|
||||||
|
aria-haspopup='listbox'
|
||||||
|
>
|
||||||
|
<span>{displayText}</span>
|
||||||
|
<svg
|
||||||
|
className={`ml-1 inline-block h-3 w-3 transition-transform duration-200 ${
|
||||||
|
pansouCloudFilterOpen ? 'rotate-180' : ''
|
||||||
|
}`}
|
||||||
|
fill='none'
|
||||||
|
stroke='currentColor'
|
||||||
|
viewBox='0 0 24 24'
|
||||||
|
aria-hidden='true'
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap='round'
|
||||||
|
strokeLinejoin='round'
|
||||||
|
strokeWidth={2}
|
||||||
|
d='M19 9l-7 7-7-7'
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!pansouCloudFilterOpen) return;
|
||||||
|
|
||||||
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
const target = event.target as Node;
|
||||||
|
if (
|
||||||
|
pansouCloudFilterButtonRef.current?.contains(target) ||
|
||||||
|
pansouCloudFilterDropdownRef.current?.contains(target)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPansouCloudFilterOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScroll = () => setPansouCloudFilterOpen(false);
|
||||||
|
const handleResize = () => calculatePansouCloudFilterPosition();
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
document.body.addEventListener('scroll', handleScroll, { passive: true });
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
document.body.removeEventListener('scroll', handleScroll);
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
};
|
||||||
|
}, [pansouCloudFilterOpen]);
|
||||||
|
|
||||||
// 返回顶部功能
|
// 返回顶部功能
|
||||||
const scrollToTop = () => {
|
const scrollToTop = () => {
|
||||||
try {
|
try {
|
||||||
@@ -1551,7 +1676,7 @@ function SearchPageClient() {
|
|||||||
<PageLayout activePath='/search'>
|
<PageLayout activePath='/search'>
|
||||||
<div className='px-4 sm:px-10 py-4 sm:py-8 overflow-visible mb-10'>
|
<div className='px-4 sm:px-10 py-4 sm:py-8 overflow-visible mb-10'>
|
||||||
{/* 搜索框 */}
|
{/* 搜索框 */}
|
||||||
<div className='mb-8'>
|
<div className='mb-0'>
|
||||||
<form onSubmit={handleSearch} className='max-w-2xl mx-auto'>
|
<form onSubmit={handleSearch} className='max-w-2xl mx-auto'>
|
||||||
<div className='relative'>
|
<div className='relative'>
|
||||||
<Search className='absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-gray-400 dark:text-gray-500' />
|
<Search className='absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-gray-400 dark:text-gray-500' />
|
||||||
@@ -1600,6 +1725,11 @@ function SearchPageClient() {
|
|||||||
router.push(
|
router.push(
|
||||||
`/search?q=${encodeURIComponent(trimmed)}&type=${activeTab}`
|
`/search?q=${encodeURIComponent(trimmed)}&type=${activeTab}`
|
||||||
);
|
);
|
||||||
|
if (activeTab === 'pansou') {
|
||||||
|
setTriggerPansouSearch((prev) => !prev);
|
||||||
|
} else if (activeTab === 'acg') {
|
||||||
|
setTriggerAcgSearch((prev) => !prev);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1639,10 +1769,65 @@ function SearchPageClient() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{activeTab === 'pansou' &&
|
||||||
|
netdiskSearchEnabled &&
|
||||||
|
renderPansouCloudTypeFilter()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{pansouCloudFilterOpen &&
|
||||||
|
createPortal(
|
||||||
|
<div
|
||||||
|
ref={pansouCloudFilterDropdownRef}
|
||||||
|
className='fixed z-[9999] max-h-[50vh] overflow-y-auto rounded-xl border border-gray-200/50 bg-white/95 p-2 backdrop-blur-sm dark:border-gray-700/50 dark:bg-gray-800/95'
|
||||||
|
style={{
|
||||||
|
left: `${pansouCloudFilterPosition.x}px`,
|
||||||
|
top: `${pansouCloudFilterPosition.y}px`,
|
||||||
|
width: `${pansouCloudFilterPosition.width}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='grid grid-cols-3 gap-1.5 sm:grid-cols-4'>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
onClick={() => setSelectedPansouCloudTypes([])}
|
||||||
|
className={`rounded-lg px-2 py-1.5 text-left text-xs transition-all duration-200 ${
|
||||||
|
selectedPansouCloudTypes.length === 0
|
||||||
|
? 'border border-green-200 bg-green-100 text-green-700 dark:border-green-700 dark:bg-green-900/30 dark:text-green-400'
|
||||||
|
: 'text-gray-700 hover:bg-gray-100/80 dark:text-gray-300 dark:hover:bg-gray-700/80'
|
||||||
|
}`}
|
||||||
|
aria-pressed={selectedPansouCloudTypes.length === 0}
|
||||||
|
>
|
||||||
|
全部类型
|
||||||
|
</button>
|
||||||
|
{PANSOU_CLOUD_TYPE_OPTIONS.map(({ value, label }) => {
|
||||||
|
const selected = selectedPansouCloudTypes.includes(value);
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={value}
|
||||||
|
type='button'
|
||||||
|
onClick={() => togglePansouCloudType(value)}
|
||||||
|
className={`rounded-lg px-2 py-1.5 text-left text-xs transition-all duration-200 ${
|
||||||
|
selected
|
||||||
|
? 'border border-green-200 bg-green-100 text-green-700 dark:border-green-700 dark:bg-green-900/30 dark:text-green-400'
|
||||||
|
: 'text-gray-700 hover:bg-gray-100/80 dark:text-gray-300 dark:hover:bg-gray-700/80'
|
||||||
|
}`}
|
||||||
|
aria-pressed={selected}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 搜索结果或搜索历史 */}
|
{/* 搜索结果或搜索历史 */}
|
||||||
<div className='max-w-[95%] mx-auto mt-12 overflow-visible'>
|
<div
|
||||||
|
className={`max-w-[95%] mx-auto overflow-visible ${
|
||||||
|
activeTab === 'pansou' ? 'mt-4' : 'mt-12'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{showResults ? (
|
{showResults ? (
|
||||||
<section className='mb-12'>
|
<section className='mb-12'>
|
||||||
{activeTab === 'video' ? (
|
{activeTab === 'video' ? (
|
||||||
@@ -1680,8 +1865,7 @@ function SearchPageClient() {
|
|||||||
</span>
|
</span>
|
||||||
{resultCountMeta.isFiltered && (
|
{resultCountMeta.isFiltered && (
|
||||||
<span className='inline-flex items-center rounded-full bg-white/80 px-2.5 py-1 font-medium text-gray-500 ring-1 ring-gray-200 dark:bg-gray-900/70 dark:text-gray-400 dark:ring-gray-700'>
|
<span className='inline-flex items-center rounded-full bg-white/80 px-2.5 py-1 font-medium text-gray-500 ring-1 ring-gray-200 dark:bg-gray-900/70 dark:text-gray-400 dark:ring-gray-700'>
|
||||||
筛选前{' '}
|
筛选前 {resultCountMeta.totalCount.toLocaleString()}{' '}
|
||||||
{resultCountMeta.totalCount.toLocaleString()}{' '}
|
|
||||||
{resultCountMeta.unit}
|
{resultCountMeta.unit}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -1851,7 +2035,9 @@ function SearchPageClient() {
|
|||||||
<VideoCard
|
<VideoCard
|
||||||
ref={getGroupRef(mapKey)}
|
ref={getGroupRef(mapKey)}
|
||||||
from='search'
|
from='search'
|
||||||
onBeforeNavigate={savePartialCacheForPlayback}
|
onBeforeNavigate={
|
||||||
|
savePartialCacheForPlayback
|
||||||
|
}
|
||||||
isAggregate={true}
|
isAggregate={true}
|
||||||
title={title}
|
title={title}
|
||||||
poster={poster}
|
poster={poster}
|
||||||
@@ -1901,7 +2087,9 @@ function SearchPageClient() {
|
|||||||
>
|
>
|
||||||
<VideoCard
|
<VideoCard
|
||||||
id={item.id}
|
id={item.id}
|
||||||
onBeforeNavigate={savePartialCacheForPlayback}
|
onBeforeNavigate={
|
||||||
|
savePartialCacheForPlayback
|
||||||
|
}
|
||||||
title={item.title}
|
title={item.title}
|
||||||
poster={item.poster}
|
poster={item.poster}
|
||||||
episodes={item.episodes.length}
|
episodes={item.episodes.length}
|
||||||
@@ -1958,6 +2146,7 @@ function SearchPageClient() {
|
|||||||
<PansouSearch
|
<PansouSearch
|
||||||
keyword={searchQuery}
|
keyword={searchQuery}
|
||||||
triggerSearch={triggerPansouSearch}
|
triggerSearch={triggerPansouSearch}
|
||||||
|
cloudTypes={selectedPansouCloudTypes}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ interface PansouSearchProps {
|
|||||||
keyword: string;
|
keyword: string;
|
||||||
triggerSearch?: boolean; // 触发搜索的标志
|
triggerSearch?: boolean; // 触发搜索的标志
|
||||||
onError?: (error: string) => void;
|
onError?: (error: string) => void;
|
||||||
|
cloudTypes?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type DownloadTool = 'aria2' | 'Transmission' | 'qBittorrent';
|
type DownloadTool = 'aria2' | 'Transmission' | 'qBittorrent';
|
||||||
@@ -31,7 +32,7 @@ const downloadToolOptions: Array<{ value: DownloadTool; label: string }> = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// 网盘类型映射
|
// 网盘类型映射
|
||||||
const CLOUD_TYPE_NAMES: Record<string, string> = {
|
export const CLOUD_TYPE_NAMES: Record<string, string> = {
|
||||||
baidu: '百度网盘',
|
baidu: '百度网盘',
|
||||||
aliyun: '阿里云盘',
|
aliyun: '阿里云盘',
|
||||||
quark: '夸克网盘',
|
quark: '夸克网盘',
|
||||||
@@ -150,6 +151,7 @@ export default function PansouSearch({
|
|||||||
keyword,
|
keyword,
|
||||||
triggerSearch,
|
triggerSearch,
|
||||||
onError,
|
onError,
|
||||||
|
cloudTypes = [],
|
||||||
}: PansouSearchProps) {
|
}: PansouSearchProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -264,6 +266,7 @@ export default function PansouSearch({
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
setResults(null);
|
setResults(null);
|
||||||
|
setSelectedType('all');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/pansou/search', {
|
const response = await fetch('/api/pansou/search', {
|
||||||
@@ -273,6 +276,7 @@ export default function PansouSearch({
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
keyword: currentKeyword,
|
keyword: currentKeyword,
|
||||||
|
cloud_types: cloudTypes,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -290,7 +294,7 @@ export default function PansouSearch({
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [keyword, onError]);
|
}, [keyword, onError, cloudTypes]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// triggerSearch 变化时触发搜索(无论是 true 还是 false)
|
// triggerSearch 变化时触发搜索(无论是 true 还是 false)
|
||||||
@@ -881,7 +885,9 @@ export default function PansouSearch({
|
|||||||
{downloadingUrl === link.url ? (
|
{downloadingUrl === link.url ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className='h-3.5 w-3.5 animate-spin' />
|
<Loader2 className='h-3.5 w-3.5 animate-spin' />
|
||||||
<span className='hidden sm:inline'>下载中...</span>
|
<span className='hidden sm:inline'>
|
||||||
|
下载中...
|
||||||
|
</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user