feat: 第一次生成
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# Nuxt Minimal Starter
|
||||
|
||||
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||
|
||||
## Setup
|
||||
|
||||
Make sure to install dependencies:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm install
|
||||
|
||||
# pnpm
|
||||
pnpm install
|
||||
|
||||
# yarn
|
||||
yarn install
|
||||
|
||||
# bun
|
||||
bun install
|
||||
```
|
||||
|
||||
## Development Server
|
||||
|
||||
Start the development server on `http://localhost:3000`:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run dev
|
||||
|
||||
# pnpm
|
||||
pnpm dev
|
||||
|
||||
# yarn
|
||||
yarn dev
|
||||
|
||||
# bun
|
||||
bun run dev
|
||||
```
|
||||
|
||||
## Production
|
||||
|
||||
Build the application for production:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run build
|
||||
|
||||
# pnpm
|
||||
pnpm build
|
||||
|
||||
# yarn
|
||||
yarn build
|
||||
|
||||
# bun
|
||||
bun run build
|
||||
```
|
||||
|
||||
Locally preview production build:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run preview
|
||||
|
||||
# pnpm
|
||||
pnpm preview
|
||||
|
||||
# yarn
|
||||
yarn preview
|
||||
|
||||
# bun
|
||||
bun run preview
|
||||
```
|
||||
|
||||
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
||||
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div class="app">
|
||||
<header class="header">
|
||||
<div class="logo-container">
|
||||
<img :src="logoImg" alt="百盘搜" class="logo">
|
||||
<h1 class="site-title">百盘搜</h1>
|
||||
</div>
|
||||
<div class="search-container">
|
||||
<input type="text" placeholder="请输入您要搜索的关键词" v-model="searchKeyword" @keyup.enter="handleSearch">
|
||||
<button @click="handleSearch" class="search-button">
|
||||
<img :src="searchIcon" alt="搜索" class="search-icon">
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div class="notice-bar">
|
||||
<span class="notice-icon">📢</span>
|
||||
<span>备用站点:</span>
|
||||
<a href="https://www.feizhupan.com" target="_blank">https://www.feizhupan.com</a>,
|
||||
<a href="https://www.dashengpan.xyz" target="_blank">https://www.dashengpan.xyz</a>,
|
||||
<span>请保存到浏览器书签中。</span>
|
||||
</div>
|
||||
<main class="main-content">
|
||||
<NuxtPage />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import logoImg from '~/assets/logo.png';
|
||||
import searchIcon from '~/assets/search-icon.svg';
|
||||
|
||||
const router = useRouter();
|
||||
const searchKeyword = ref('');
|
||||
|
||||
function handleSearch() {
|
||||
if (searchKeyword.value.trim()) {
|
||||
router.push({ path: '/', query: { keyword: searchKeyword.value } });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 2rem;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.site-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #ff9500;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
max-width: 600px;
|
||||
border: 2px solid #ff9500;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.search-container input {
|
||||
flex-grow: 1;
|
||||
padding: 0.6rem 1rem;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.search-button {
|
||||
background-color: #ff9500;
|
||||
border: none;
|
||||
padding: 0.6rem 1rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.notice-bar {
|
||||
background-color: #fff8e6;
|
||||
padding: 0.5rem 2rem;
|
||||
color: #9e6500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.notice-bar a {
|
||||
color: #ff9500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 2rem;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 278 B |
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="result-item">
|
||||
<div class="result-icon">
|
||||
<img :src="getFileTypeIcon(item.fileType)" :alt="item.fileType" />
|
||||
</div>
|
||||
<div class="result-content">
|
||||
<h3 class="result-title">{{ item.title }}</h3>
|
||||
<div class="result-details">
|
||||
<div v-if="item.fileList && item.fileList.length" class="file-list">
|
||||
<div v-for="(file, index) in visibleFiles" :key="index" class="file-item">
|
||||
<span>├ {{ file.name }} - {{ formatSize(file.size) }}</span>
|
||||
</div>
|
||||
<div v-if="item.fileList.length > maxVisibleFiles" class="more-files">
|
||||
...还有{{ item.fileList.length - maxVisibleFiles }}个文件
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-meta">
|
||||
<span class="size">文件大小: {{ formatSize(item.size) }}</span>
|
||||
<span class="date">更新时间: {{ item.updateTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-actions">
|
||||
<button class="get-link-button" @click="$emit('get-link', item)">
|
||||
获取链接
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface File {
|
||||
name: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
interface SearchResult {
|
||||
id: string;
|
||||
title: string;
|
||||
fileType: string;
|
||||
size: number;
|
||||
updateTime: string;
|
||||
cloudType: string;
|
||||
fileList?: File[];
|
||||
}
|
||||
|
||||
const props = defineProps<{ item: SearchResult }>()
|
||||
const emit = defineEmits<{ (e: 'get-link', item: SearchResult): void }>()
|
||||
|
||||
const maxVisibleFiles = 5
|
||||
|
||||
const visibleFiles = computed(() => {
|
||||
if (!props.item.fileList) return []
|
||||
return props.item.fileList.slice(0, maxVisibleFiles)
|
||||
})
|
||||
|
||||
function formatSize(bytes: number) {
|
||||
if (bytes === 0) return '0 B'
|
||||
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024))
|
||||
|
||||
return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + units[i]
|
||||
}
|
||||
|
||||
function getFileTypeIcon(fileType: string) {
|
||||
const icons: Record<string, string> = {
|
||||
folder: '/icons/folder.png',
|
||||
document: '/icons/document.png',
|
||||
video: '/icons/video.png',
|
||||
audio: '/icons/audio.png',
|
||||
image: '/icons/image.png',
|
||||
}
|
||||
|
||||
return icons[fileType] || '/icons/file.png'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.result-item {
|
||||
display: flex;
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.result-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.result-icon img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.file-list {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
margin-bottom: 4px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.more-files {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
font-size: 13px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.result-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.result-actions {
|
||||
margin-left: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.get-link-button {
|
||||
background-color: #ff9500;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.get-link-button:hover {
|
||||
background-color: #e68600;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,5 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2024-11-01',
|
||||
devtools: { enabled: true }
|
||||
})
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "nuxt-app",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"nuxt": "^3.17.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,637 @@
|
||||
<template>
|
||||
<div class="search-page">
|
||||
<div class="filter-bar">
|
||||
<div>
|
||||
筛选:
|
||||
<div class="filter-group">
|
||||
<span>模式</span>
|
||||
<select v-model="filterMode">
|
||||
<option value="standard">精准搜索</option>
|
||||
<option value="fuzzy">模糊搜索</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<span>文件类型</span>
|
||||
<select v-model="fileType">
|
||||
<option value="">全部</option>
|
||||
<option value="document">文档</option>
|
||||
<option value="video">视频</option>
|
||||
<option value="audio">音频</option>
|
||||
<option value="image">图片</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<span>文件大小</span>
|
||||
<select v-model="fileSize">
|
||||
<option value="">全部</option>
|
||||
<option value="small">小(< 10MB)</option>
|
||||
<option value="medium">中(10MB-100MB)</option>
|
||||
<option value="large">大(100MB-1GB)</option>
|
||||
<option value="huge">超大(> 1GB)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<span>文件时间</span>
|
||||
<select v-model="fileTime">
|
||||
<option value="">全部</option>
|
||||
<option value="today">今天</option>
|
||||
<option value="week">本周</option>
|
||||
<option value="month">本月</option>
|
||||
<option value="year">今年</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<button class="clear-button" @click="clearFilters">
|
||||
<span>清除</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cloud-filter">
|
||||
<div>
|
||||
网盘:
|
||||
<button
|
||||
v-for="drive in driveTypes"
|
||||
:key="drive.value"
|
||||
:class="['cloud-button', { active: selectedDrives.includes(drive.value) }]"
|
||||
@click="toggleDrive(drive.value)"
|
||||
>
|
||||
<img :src="drive.icon" :alt="drive.label" class="cloud-icon" />
|
||||
<span>{{ drive.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-tips" v-if="!searched">
|
||||
<h3>搜索小技巧:</h3>
|
||||
<ol>
|
||||
<li>可以灵活选用「精准搜索」和「模糊搜索」</li>
|
||||
<li>搜索资源时可以通过资源「类型」、「时间」等进行筛选</li>
|
||||
<li>增加「文件来源」如(百度网盘、阿里云盘等)进行筛选</li>
|
||||
<li>搜索关键次尽量不要包含「的」「与」等无关助词,只包含关键词即可</li>
|
||||
<li>如搜索过于频繁被屏蔽,请发送UID信息给shakanamo945@gmail.com申请解封</li>
|
||||
<li>如有希望我们重点关注或收录的资源网站来源(限免费开放式网站),请发送邮件至:zhuangbee8287@163.com</li>
|
||||
<li>相关版权方对于搜索关键词屏蔽需求请通过发送邮件至:shakanamo945@gmail.com;</li>
|
||||
<li>坚决抵制劣质、违规、隐私、风险版权、其他问题资源,请大家发现一律通过页面举报;</li>
|
||||
<li>备用站点 <a href="https://www.feizhupan.com">https://www.feizhupan.com</a>,<a href="https://www.dashengpan.xyz">https://www.dashengpan.xyz</a>;</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div v-if="searched">
|
||||
<div v-if="results.length === 0" class="no-results">
|
||||
<p>没有找到相关结果,请尝试其他关键词或筛选条件</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="results-container">
|
||||
<div v-for="(group, index) in groupedResults" :key="index" class="result-group">
|
||||
<div class="cloud-header">
|
||||
<img :src="getCloudIcon(group.cloudType)" :alt="group.cloudType" class="cloud-icon" />
|
||||
<span class="cloud-name">{{ getCloudName(group.cloudType) }}</span>
|
||||
<span class="result-count">共搜出 {{ group.items.length }} 条结果</span>
|
||||
</div>
|
||||
|
||||
<div class="result-list">
|
||||
<div v-for="item in group.items" :key="item.id" class="result-item">
|
||||
<div class="result-icon">
|
||||
<img :src="getFileTypeIcon(item.fileType)" :alt="item.fileType" />
|
||||
</div>
|
||||
<div class="result-content">
|
||||
<div class="result-title">{{ item.title }}</div>
|
||||
<div class="result-details">
|
||||
<div v-if="item.fileList && item.fileList.length" class="file-list">
|
||||
<div v-for="file in item.fileList" :key="file.name" class="file-item">
|
||||
<span>├ {{ file.name }} - {{ formatSize(file.size) }}</span>
|
||||
</div>
|
||||
<div v-if="item.fileList.length > 5" class="more-files">
|
||||
...
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-meta">
|
||||
<span>文件大小: {{ formatSize(item.size) }}</span>
|
||||
<span>更新时间: {{ formatDate(item.updateTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="result-actions">
|
||||
<button class="get-link-button" @click="getLink(item)">
|
||||
获取链接
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pagination" v-if="totalPages > 1">
|
||||
<button :disabled="page === 1" @click="changePage(page - 1)">上一页</button>
|
||||
<div class="page-numbers">
|
||||
<button
|
||||
v-for="p in displayedPages"
|
||||
:key="p"
|
||||
:class="['page-number', { active: p === page }]"
|
||||
@click="changePage(p)"
|
||||
>
|
||||
{{ p }}
|
||||
</button>
|
||||
</div>
|
||||
<button :disabled="page === totalPages" @click="changePage(page + 1)">下一页</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const keyword = ref('')
|
||||
const filterMode = ref('standard')
|
||||
const fileType = ref('')
|
||||
const fileSize = ref('')
|
||||
const fileTime = ref('')
|
||||
const selectedDrives = ref<string[]>(['baidu', 'aliyun', 'quark', 'xunlei'])
|
||||
const page = ref(1)
|
||||
const totalPages = ref(1)
|
||||
const results = ref<any[]>([])
|
||||
const searched = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
const driveTypes = [
|
||||
{ value: 'baidu', label: '百度网盘', icon: '/images/baidu.png' },
|
||||
{ value: 'aliyun', label: '阿里云盘', icon: '/images/aliyun.png' },
|
||||
{ value: 'quark', label: '夸克网盘', icon: '/images/quark.png' },
|
||||
{ value: 'xunlei', label: '迅雷网盘', icon: '/images/xunlei.png' },
|
||||
]
|
||||
|
||||
// 模拟数据
|
||||
const mockResults = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Deepseek',
|
||||
fileType: 'folder',
|
||||
size: 312 * 1024 * 1024 * 1024,
|
||||
updateTime: '2025-04-15',
|
||||
cloudType: 'quark',
|
||||
fileList: [
|
||||
{ name: 'deepseek.apk', size: 8.8 * 1024 * 1024 },
|
||||
{ name: 'DeepSeek.dmg', size: 14.6 * 1024 * 1024 },
|
||||
{ name: 'DeepSeek_x64.msi', size: 6.1 * 1024 * 1024 },
|
||||
{ name: 'DeepSeek15天指导手册.pdf', size: 1.3 * 1024 * 1024 },
|
||||
{ name: 'DeepSeek_x86_64.deb', size: 8.2 * 1024 * 1024 },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Deepseek',
|
||||
fileType: 'folder',
|
||||
size: 311.8 * 1024 * 1024 * 1024,
|
||||
updateTime: '2025-03-07',
|
||||
cloudType: 'quark',
|
||||
fileList: [
|
||||
{ name: 'deepseek.apk', size: 8.8 * 1024 * 1024 },
|
||||
{ name: 'DeepSeek.dmg', size: 14.6 * 1024 * 1024 },
|
||||
{ name: 'DeepSeek_x64.msi', size: 6.1 * 1024 * 1024 },
|
||||
{ name: 'DeepSeek15天指导手册.pdf', size: 1.3 * 1024 * 1024 },
|
||||
{ name: 'DeepSeek_x86_64.deb', size: 8.2 * 1024 * 1024 },
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const groupedResults = computed(() => {
|
||||
const groups: Record<string, { cloudType: string, items: any[] }> = {}
|
||||
|
||||
results.value.forEach(result => {
|
||||
if (!groups[result.cloudType]) {
|
||||
groups[result.cloudType] = {
|
||||
cloudType: result.cloudType,
|
||||
items: []
|
||||
}
|
||||
}
|
||||
groups[result.cloudType].items.push(result)
|
||||
})
|
||||
|
||||
return Object.values(groups)
|
||||
})
|
||||
|
||||
const displayedPages = computed(() => {
|
||||
const totalDisplayed = 5
|
||||
const currentPage = page.value
|
||||
const total = totalPages.value
|
||||
|
||||
if (total <= totalDisplayed) {
|
||||
return Array.from({ length: total }, (_, i) => i + 1)
|
||||
}
|
||||
|
||||
let start = Math.max(currentPage - Math.floor(totalDisplayed / 2), 1)
|
||||
let end = start + totalDisplayed - 1
|
||||
|
||||
if (end > total) {
|
||||
end = total
|
||||
start = Math.max(end - totalDisplayed + 1, 1)
|
||||
}
|
||||
|
||||
return Array.from({ length: end - start + 1 }, (_, i) => start + i)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const queryKeyword = route.query.keyword as string
|
||||
if (queryKeyword) {
|
||||
keyword.value = queryKeyword
|
||||
onSearch()
|
||||
}
|
||||
})
|
||||
|
||||
function toggleDrive(drive: string) {
|
||||
const index = selectedDrives.value.indexOf(drive)
|
||||
if (index === -1) {
|
||||
selectedDrives.value.push(drive)
|
||||
} else {
|
||||
selectedDrives.value.splice(index, 1)
|
||||
}
|
||||
|
||||
if (searched.value) {
|
||||
fetchResults()
|
||||
}
|
||||
}
|
||||
|
||||
function clearFilters() {
|
||||
filterMode.value = 'standard'
|
||||
fileType.value = ''
|
||||
fileSize.value = ''
|
||||
fileTime.value = ''
|
||||
selectedDrives.value = ['baidu', 'aliyun', 'quark', 'xunlei']
|
||||
|
||||
if (searched.value) {
|
||||
fetchResults()
|
||||
}
|
||||
}
|
||||
|
||||
async function onSearch() {
|
||||
if (!keyword.value.trim()) return
|
||||
|
||||
page.value = 1
|
||||
await fetchResults()
|
||||
}
|
||||
|
||||
async function fetchResults() {
|
||||
loading.value = true
|
||||
searched.value = true
|
||||
|
||||
try {
|
||||
// 构建API请求参数
|
||||
const params = new URLSearchParams();
|
||||
if (keyword.value) params.append('keyword', keyword.value);
|
||||
selectedDrives.value.forEach(drive => {
|
||||
params.append('types', drive);
|
||||
});
|
||||
params.append('page', page.value.toString());
|
||||
if (fileType.value) params.append('fileType', fileType.value);
|
||||
if (fileSize.value) params.append('fileSize', fileSize.value);
|
||||
if (fileTime.value) params.append('fileTime', fileTime.value);
|
||||
params.append('mode', filterMode.value);
|
||||
|
||||
// 调用后端API
|
||||
const response = await fetch(`/api/search?${params.toString()}`);
|
||||
const responseData = await response.json();
|
||||
|
||||
if (responseData.success) {
|
||||
results.value = responseData.data;
|
||||
totalPages.value = responseData.totalPages || 1;
|
||||
} else {
|
||||
console.error('搜索失败:', responseData.message);
|
||||
results.value = [];
|
||||
}
|
||||
|
||||
// 模拟数据(当后端未启动时)
|
||||
if (results.value.length === 0) {
|
||||
results.value = mockResults;
|
||||
totalPages.value = 5;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索失败', error);
|
||||
// 使用模拟数据
|
||||
results.value = mockResults;
|
||||
totalPages.value = 5;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function changePage(newPage: number) {
|
||||
page.value = newPage
|
||||
|
||||
// 更新URL但不触发路由变化
|
||||
router.replace({
|
||||
query: {
|
||||
...route.query,
|
||||
page: newPage.toString()
|
||||
}
|
||||
})
|
||||
|
||||
fetchResults()
|
||||
}
|
||||
|
||||
async function getLink(item: any) {
|
||||
try {
|
||||
// 调用后端生成链接API
|
||||
const response = await fetch(`/api/link/${item.id}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success && data.data.link) {
|
||||
// 显示链接
|
||||
alert(`获取链接成功: ${data.data.link}\n有效期: ${data.data.expiresIn}秒`);
|
||||
} else {
|
||||
alert(`获取链接失败: ${data.message || '未知错误'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取链接失败', error);
|
||||
alert(`获取链接失败: 服务异常,请稍后再试`);
|
||||
}
|
||||
}
|
||||
|
||||
function getCloudIcon(cloudType: string) {
|
||||
const drive = driveTypes.find(d => d.value === cloudType)
|
||||
return drive?.icon || '/icons/default-cloud.png'
|
||||
}
|
||||
|
||||
function getCloudName(cloudType: string) {
|
||||
const drive = driveTypes.find(d => d.value === cloudType)
|
||||
return drive?.label || cloudType
|
||||
}
|
||||
|
||||
function getFileTypeIcon(fileType: string) {
|
||||
const icons: Record<string, string> = {
|
||||
folder: '/icons/folder.png',
|
||||
document: '/icons/document.png',
|
||||
video: '/icons/video.png',
|
||||
audio: '/icons/audio.png',
|
||||
image: '/icons/image.png',
|
||||
}
|
||||
|
||||
return icons[fileType] || '/icons/file.png'
|
||||
}
|
||||
|
||||
function formatSize(bytes: number) {
|
||||
if (bytes === 0) return '0 B'
|
||||
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024))
|
||||
|
||||
return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + units[i]
|
||||
}
|
||||
|
||||
function formatDate(date: string) {
|
||||
return date
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-page {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.filter-bar, .cloud-filter {
|
||||
background-color: #fff;
|
||||
padding: 12px 16px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.filter-group span {
|
||||
margin-right: 8px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.filter-group select {
|
||||
padding: 6px 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.clear-button {
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cloud-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: #f9f9f9;
|
||||
margin-right: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cloud-button.active {
|
||||
background-color: #e6f7ff;
|
||||
border-color: #1890ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.cloud-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.search-tips {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.search-tips h3 {
|
||||
margin-bottom: 12px;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.search-tips ol {
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.search-tips li {
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.5;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.search-tips a {
|
||||
color: #1890ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
background-color: #fff;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.results-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.result-group {
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cloud-header {
|
||||
padding: 12px 16px;
|
||||
background-color: #f9f9f9;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cloud-name {
|
||||
font-weight: 500;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.result-count {
|
||||
color: #ff9500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.result-list {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
display: flex;
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.result-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.result-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.result-icon img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.result-details {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.file-list {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
margin-bottom: 4px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.more-files {
|
||||
color: #888;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.result-meta {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.result-actions {
|
||||
margin-left: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.get-link-button {
|
||||
background-color: #ff9500;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.pagination button {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #ddd;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pagination button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.page-numbers {
|
||||
display: flex;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.page-number {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.page-number.active {
|
||||
background-color: #ff9500;
|
||||
color: white;
|
||||
border-color: #ff9500;
|
||||
}
|
||||
</style>
|
||||
Generated
+7862
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,2 @@
|
||||
User-Agent: *
|
||||
Disallow:
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../.nuxt/tsconfig.server.json"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
// https://nuxt.com/docs/guide/concepts/typescript
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
||||
Reference in New Issue
Block a user