first commit
This commit is contained in:
commit
349a4538b1
24
.eslintrc.js
Normal file
24
.eslintrc.js
Normal file
@ -0,0 +1,24 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
browser: true,
|
||||
es2021: true,
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-prettier',
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2021,
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/no-unused-components': 'warn',
|
||||
},
|
||||
}
|
9
.prettierrc
Normal file
9
.prettierrc
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"endOfLine": "auto"
|
||||
}
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Pan Search
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
111
README.md
Normal file
111
README.md
Normal file
@ -0,0 +1,111 @@
|
||||
# 绝绝子网盘搜索
|
||||
|
||||
一个简洁的网盘资源搜索工具,支持多平台资源搜索,提供现代化的用户界面和流畅的搜索体验。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- 🔍 快速搜索:支持多平台网盘资源搜索
|
||||
- 🎨 现代化界面:采用 Material Design 设计风格
|
||||
- 🌙 深色模式:支持自动切换深色/浅色主题
|
||||
- 📱 响应式设计:完美适配各种设备尺寸
|
||||
- ⚡ 性能优化:快速加载,流畅体验
|
||||
|
||||
## 技术栈
|
||||
|
||||
- Vue 3 - 前端框架
|
||||
- Vite - 构建工具
|
||||
- TailwindCSS - 样式框架
|
||||
- Axios - HTTP 客户端
|
||||
- Vue Router - 路由管理
|
||||
- Pinia - 状态管理
|
||||
|
||||
## 开发指南
|
||||
|
||||
### 环境要求
|
||||
|
||||
- Node.js >= 16.0.0
|
||||
- npm >= 7.0.0
|
||||
|
||||
### 安装依赖
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### 开发环境
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 构建生产版本
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
## 部署说明
|
||||
|
||||
### 1. 构建项目
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
构建完成后,文件将生成在 `dist` 目录中。
|
||||
|
||||
### 2. 配置 Nginx
|
||||
|
||||
项目提供了一个 `nginx.conf.template` 配置模板文件,使用时需要:
|
||||
|
||||
1. 复制 `nginx.conf.template` 文件并重命名为 `nginx.conf`
|
||||
2. 替换文件中的以下占位符:
|
||||
- `[你的域名]`: 网站域名,如 `pan.example.com`
|
||||
- `[你的网站根目录]`: 网站文件存放目录,如 `/www/wwwroot/pan.example.com`
|
||||
- `[日志目录]`: 日志文件存放目录,如 `/www/wwwlogs/pan.example.com`
|
||||
- `[目标API地址]`: API 服务器地址,如 `https://api.example.com/`
|
||||
- `[允许的域名]`: 允许跨域访问的域名,如 `https://pan.example.com`
|
||||
|
||||
示例配置:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name pan.example.com;
|
||||
root /www/wwwroot/pan.example.com;
|
||||
index index.html;
|
||||
|
||||
access_log /www/wwwlogs/pan.example.com/access.log;
|
||||
error_log /www/wwwlogs/pan.example.com/error.log;
|
||||
|
||||
location /api/ {
|
||||
proxy_pass https://api.example.com/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
add_header 'Access-Control-Allow-Origin' 'https://pan.example.com' always;
|
||||
# ... 其他配置 ...
|
||||
}
|
||||
|
||||
# ... 其他配置 ...
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 上传文件
|
||||
|
||||
将 `dist` 目录中的所有文件上传到服务器的网站根目录。
|
||||
|
||||
### 4. 配置域名
|
||||
|
||||
确保域名已正确解析到服务器IP,并在服务器上配置好SSL证书(如果需要)。
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 在部署到生产环境之前,请确保所有占位符都已正确替换
|
||||
- 建议启用 HTTPS 以提升安全性
|
||||
- 定期备份网站数据和日志文件
|
||||
- 监控服务器资源使用情况,及时优化配置
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
15
index.html
Normal file
15
index.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>绝绝子网盘搜索</title>
|
||||
<meta name="description" content="专注于收录全网云盘资源,支持七大网盘搜索">
|
||||
<meta name="keywords" content="网盘搜索,百度网盘搜索,阿里云盘搜索,夸克网盘搜索,资源搜索">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
58
nginx.conf.template
Normal file
58
nginx.conf.template
Normal file
@ -0,0 +1,58 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name [你的域名];
|
||||
root [你的网站根目录];
|
||||
index index.html;
|
||||
|
||||
# 日志配置
|
||||
access_log [日志目录]/access.log;
|
||||
error_log [日志目录]/error.log;
|
||||
|
||||
# API 反向代理
|
||||
location /api/ {
|
||||
proxy_pass https://www.yunso.net/api/;
|
||||
proxy_set_header Host www.yunso.net;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# CORS 设置
|
||||
add_header 'Access-Control-Allow-Origin' '[允许的域名]' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always;
|
||||
add_header 'Access-Control-Max-Age' 1728000 always;
|
||||
add_header 'Content-Type' 'application/json charset=UTF-8' always;
|
||||
|
||||
# 处理 OPTIONS 请求
|
||||
if ($request_method = 'OPTIONS') {
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# 静态文件缓存
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, no-transform";
|
||||
}
|
||||
|
||||
# 前端路由
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# 禁止访问敏感文件
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
|
||||
location ~ /package\.json$ {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# Gzip 压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1000;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
}
|
3897
package-lock.json
generated
Normal file
3897
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
package.json
Normal file
32
package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "juejuezi-new",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^10.3.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"axios": "^1.4.0",
|
||||
"pinia": "^2.1.6",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-plugin-vue": "^9.15.1",
|
||||
"prettier": "^3.0.0",
|
||||
"terser": "^5.39.0",
|
||||
"vite": "^4.4.5"
|
||||
}
|
||||
}
|
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
51
src/App.vue
Normal file
51
src/App.vue
Normal file
@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex flex-col">
|
||||
<header class="bg-white shadow-sm dark:bg-gray-800">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<h1 class="text-2xl font-bold text-primary-600">{{ siteName }}</h1>
|
||||
</div>
|
||||
<div class="flex items-center space-x-4">
|
||||
<button @click="toggleDarkMode" class="p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg v-if="isDark" class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||
</svg>
|
||||
<svg v-else class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="flex-grow">
|
||||
<router-view />
|
||||
</main>
|
||||
|
||||
<footer class="bg-white dark:bg-gray-800">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<p class="text-center text-gray-500 dark:text-gray-400">
|
||||
© {{ new Date().getFullYear() }} {{ siteName }}. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
|
||||
const siteName = '绝绝子网盘搜索'
|
||||
const isDark = useDark()
|
||||
const toggleDarkMode = useToggle(isDark)
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化主题
|
||||
if (localStorage.getItem('darkMode') === 'true') {
|
||||
document.documentElement.classList.add('dark')
|
||||
}
|
||||
})
|
||||
</script>
|
35
src/assets/main.css
Normal file
35
src/assets/main.css
Normal file
@ -0,0 +1,35 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
@apply antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-gray-50 text-gray-900 dark:bg-gray-900 dark:text-gray-100;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply px-4 py-2 rounded-md font-medium transition-colors duration-200;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply bg-primary-600 text-white hover:bg-primary-700;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply bg-gray-200 text-gray-800 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600;
|
||||
}
|
||||
|
||||
.input {
|
||||
@apply w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent dark:bg-gray-800 dark:border-gray-700;
|
||||
}
|
||||
|
||||
.card {
|
||||
@apply bg-white rounded-lg shadow-md p-6 dark:bg-gray-800;
|
||||
}
|
||||
}
|
13
src/config/index.js
Normal file
13
src/config/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
export const config = {
|
||||
// 网站配置
|
||||
siteName: '绝绝子网盘搜索',
|
||||
siteDescription: '专注于收录全网云盘资源,支持七大网盘搜索',
|
||||
siteKeywords: '网盘搜索,百度网盘搜索,阿里云盘搜索,夸克网盘搜索,资源搜索',
|
||||
|
||||
// API配置
|
||||
api: {
|
||||
search: '/opensearch.php',
|
||||
data: '/opendata.php',
|
||||
dataEntry: '/opendataentry.php'
|
||||
}
|
||||
}
|
12
src/main.js
Normal file
12
src/main.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import './assets/main.css'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
41
src/router/index.js
Normal file
41
src/router/index.js
Normal file
@ -0,0 +1,41 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Home from '../views/Home.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home,
|
||||
meta: {
|
||||
title: '绝绝子网盘搜索 - 专注网盘资源搜索'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'About',
|
||||
component: () => import('../views/About.vue'),
|
||||
meta: {
|
||||
title: '关于我们 - 绝绝子网盘搜索'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
} else {
|
||||
return { top: 0 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 设置页面标题
|
||||
router.beforeEach((to, from, next) => {
|
||||
document.title = to.meta.title || '绝绝子网盘搜索'
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
44
src/utils/axios.js
Normal file
44
src/utils/axios.js
Normal file
@ -0,0 +1,44 @@
|
||||
import axios from 'axios'
|
||||
import { config } from '../config'
|
||||
|
||||
// 创建 axios 实例
|
||||
const instance = axios.create({
|
||||
baseURL: '/api',
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
instance.interceptors.request.use(
|
||||
config => {
|
||||
// 添加时间戳防止缓存
|
||||
if (config.method === 'get') {
|
||||
config.params = {
|
||||
...config.params,
|
||||
_t: new Date().getTime()
|
||||
}
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
console.error('Request error:', error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
instance.interceptors.response.use(
|
||||
response => {
|
||||
return response
|
||||
},
|
||||
error => {
|
||||
console.error('Response error:', error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default instance
|
55
src/views/About.vue
Normal file
55
src/views/About.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="card">
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-6">关于我们</h1>
|
||||
|
||||
<div class="prose dark:prose-invert max-w-none">
|
||||
<p class="mb-4">
|
||||
绝绝子网盘搜索是一个专注于网盘资源搜索的平台,致力于为用户提供快速、准确的网盘资源搜索服务。
|
||||
</p>
|
||||
|
||||
<h2 class="text-xl font-semibold mt-8 mb-4">支持网盘</h2>
|
||||
<ul class="list-disc pl-6 mb-6">
|
||||
<li>阿里云盘</li>
|
||||
<li>夸克网盘</li>
|
||||
<li>百度网盘</li>
|
||||
<li>迅雷网盘</li>
|
||||
<li>天翼云盘</li>
|
||||
<li>蓝奏云</li>
|
||||
<li>其他主流网盘</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="text-xl font-semibold mt-8 mb-4">使用说明</h2>
|
||||
<ol class="list-decimal pl-6 mb-6">
|
||||
<li>在搜索框输入关键词</li>
|
||||
<li>点击搜索按钮或按回车键</li>
|
||||
<li>查看搜索结果</li>
|
||||
<li>点击"查看资源"按钮访问网盘链接</li>
|
||||
</ol>
|
||||
|
||||
<h2 class="text-xl font-semibold mt-8 mb-4">注意事项</h2>
|
||||
<ul class="list-disc pl-6 mb-6">
|
||||
<li>请遵守相关法律法规,合理使用搜索服务</li>
|
||||
<li>部分资源可能需要提取码,请仔细查看</li>
|
||||
<li>资源链接有效性由资源提供者负责</li>
|
||||
<li>如发现违规内容,请联系我们处理</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="text-xl font-semibold mt-8 mb-4">联系我们</h2>
|
||||
<p class="mb-4">
|
||||
如果您有任何问题或建议,欢迎通过以下方式联系我们:
|
||||
</p>
|
||||
<ul class="list-disc pl-6">
|
||||
<li>邮箱:support@juejuezi.cc</li>
|
||||
<li>反馈:通过网站底部的反馈按钮</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 页面逻辑
|
||||
</script>
|
228
src/views/Home.vue
Normal file
228
src/views/Home.vue
Normal file
@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<!-- Logo 和标题区域 -->
|
||||
<div class="text-center mb-12">
|
||||
<div class="flex items-center justify-center mb-6">
|
||||
<svg class="w-12 h-12 text-primary-600 mr-4" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 6.5C4 5.67157 4.67157 5 5.5 5H18.5C19.3284 5 20 5.67157 20 6.5V17.5C20 18.3284 19.3284 19 18.5 19H5.5C4.67157 19 4 18.3284 4 17.5V6.5Z" stroke="currentColor" stroke-width="2"/>
|
||||
<path d="M8 12H16M8 15H13" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M8 9H16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<h1 class="text-4xl md:text-5xl font-bold bg-gradient-to-r from-primary-600 to-primary-400 bg-clip-text text-transparent">
|
||||
{{ config.siteName }}
|
||||
</h1>
|
||||
</div>
|
||||
<p class="text-lg text-gray-600 mb-2">
|
||||
{{ config.siteDescription }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-500">
|
||||
已收录 10000000+ 网盘资源免费分享
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 搜索框区域 -->
|
||||
<div class="card mb-8 border-2 border-primary-100">
|
||||
<div class="relative">
|
||||
<input
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
class="input pr-24 text-lg h-14"
|
||||
placeholder="输入关键词搜索资源..."
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
<button
|
||||
@click="handleSearch"
|
||||
class="absolute right-2 top-1/2 transform -translate-y-1/2 btn btn-primary h-10 flex items-center"
|
||||
:disabled="loading"
|
||||
>
|
||||
<svg v-if="loading" class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<span>{{ loading ? '搜索中' : '搜索' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快捷分类标签 -->
|
||||
<div class="mb-8">
|
||||
<div class="flex flex-wrap gap-2 justify-center">
|
||||
<button
|
||||
v-for="tag in quickTags"
|
||||
:key="tag"
|
||||
@click="quickSearch(tag)"
|
||||
class="px-3 py-1 rounded-full text-sm bg-gray-100 hover:bg-primary-100 text-gray-600 hover:text-primary-600 transition-colors duration-200"
|
||||
>
|
||||
{{ tag }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 错误提示 -->
|
||||
<div v-if="error" class="card mb-8 bg-red-50">
|
||||
<div class="text-red-600">
|
||||
{{ error }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 加载动画 -->
|
||||
<div v-if="loading" class="text-center py-12">
|
||||
<div class="inline-flex items-center px-4 py-2 font-semibold leading-6 text-primary-600 transition ease-in-out duration-150 cursor-not-allowed">
|
||||
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-primary-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
正在搜索中...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索结果 -->
|
||||
<div v-else-if="searchResults.length > 0" class="space-y-4">
|
||||
<div
|
||||
v-for="(result, index) in searchResults"
|
||||
:key="index"
|
||||
class="card hover:shadow-lg transition-shadow duration-200 border border-gray-100"
|
||||
>
|
||||
<div class="cursor-pointer" @click="toggleExpand(index)">
|
||||
<h3 class="text-base md:text-lg font-semibold text-gray-900"
|
||||
:class="{ 'line-clamp-2': !expandedItems[index] }">
|
||||
{{ removeEmoji(result.ScrName) }}
|
||||
</h3>
|
||||
<div v-if="!expandedItems[index] && isLongText(removeEmoji(result.ScrName))"
|
||||
class="text-primary-600 text-sm mt-1 hover:text-primary-700">
|
||||
点击展开全文
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2 md:gap-4 text-sm text-gray-500 my-4">
|
||||
<span class="flex items-center">
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
{{ formatTime(result.addtime) }}
|
||||
</span>
|
||||
<span class="flex items-center">
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
|
||||
</svg>
|
||||
{{ result.Scrurlname }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-3 md:gap-4">
|
||||
<a
|
||||
:href="result.Scrurl"
|
||||
target="_blank"
|
||||
class="btn btn-primary flex items-center text-sm md:text-base"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
||||
</svg>
|
||||
查看资源
|
||||
</a>
|
||||
<span v-if="result.Scrpass" class="flex items-center text-sm text-gray-500">
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
|
||||
</svg>
|
||||
提取码: {{ result.Scrpass }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无搜索结果 -->
|
||||
<div v-else-if="searched" class="text-center py-12">
|
||||
<div class="inline-block">
|
||||
<svg class="w-16 h-16 text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<p class="text-gray-500 text-lg">未找到相关资源,换个关键词试试?</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
import http from '../utils/axios'
|
||||
import { config } from '../config'
|
||||
|
||||
const searchQuery = ref('')
|
||||
const searchResults = ref([])
|
||||
const loading = ref(false)
|
||||
const searched = ref(false)
|
||||
const error = ref('')
|
||||
const expandedItems = reactive({})
|
||||
|
||||
// 快捷搜索标签
|
||||
const quickTags = [
|
||||
'电影', '音乐', '小说', '动漫',
|
||||
'软件', '教程', '设计', '游戏'
|
||||
]
|
||||
|
||||
// 移除 emoji 标签
|
||||
const removeEmoji = (text) => {
|
||||
if (!text) return ''
|
||||
// 使用正则表达式移除所有 [@emoji=xxx] 格式的内容
|
||||
return text.replace(/\[@emoji=[^\]]*\]/g, '')
|
||||
}
|
||||
|
||||
const handleSearch = async () => {
|
||||
if (!searchQuery.value.trim()) return
|
||||
|
||||
loading.value = true
|
||||
searched.value = true
|
||||
searchResults.value = []
|
||||
error.value = ''
|
||||
// 重置展开状态
|
||||
Object.keys(expandedItems).forEach(key => delete expandedItems[key])
|
||||
|
||||
try {
|
||||
const response = await http.get(config.api.search, {
|
||||
params: {
|
||||
wd: searchQuery.value
|
||||
}
|
||||
})
|
||||
|
||||
if (response.data.code === 0) {
|
||||
searchResults.value = response.data.Data || []
|
||||
} else {
|
||||
error.value = response.data.msg || '搜索失败,请稍后重试'
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('搜索出错:', err)
|
||||
error.value = err.response?.data?.message || '搜索出错,请稍后重试'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const quickSearch = (tag) => {
|
||||
searchQuery.value = tag
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
const formatTime = (timestamp) => {
|
||||
const date = new Date(parseInt(timestamp))
|
||||
return date.toLocaleString()
|
||||
}
|
||||
|
||||
const toggleExpand = (index) => {
|
||||
expandedItems[index] = !expandedItems[index]
|
||||
}
|
||||
|
||||
const isLongText = (text) => {
|
||||
return text && text.length > 50
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
29
tailwind.config.js
Normal file
29
tailwind.config.js
Normal file
@ -0,0 +1,29 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#f0f9ff',
|
||||
100: '#e0f2fe',
|
||||
200: '#bae6fd',
|
||||
300: '#7dd3fc',
|
||||
400: '#38bdf8',
|
||||
500: '#0ea5e9',
|
||||
600: '#0284c7',
|
||||
700: '#0369a1',
|
||||
800: '#075985',
|
||||
900: '#0c4a6e',
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter var', 'sans-serif'],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
49
vite.config.js
Normal file
49
vite.config.js
Normal file
@ -0,0 +1,49 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import path from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://www.yunso.net',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, '')
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
assetsDir: 'assets',
|
||||
sourcemap: false,
|
||||
minify: 'terser',
|
||||
terserOptions: {
|
||||
compress: {
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
},
|
||||
},
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: path.resolve(__dirname, 'index.html'),
|
||||
},
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vue': ['vue', 'vue-router', 'pinia'],
|
||||
'vendor': ['axios', '@vueuse/core'],
|
||||
},
|
||||
chunkFileNames: 'assets/js/[name]-[hash].js',
|
||||
entryFileNames: 'assets/js/[name]-[hash].js',
|
||||
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
|
||||
},
|
||||
},
|
||||
},
|
||||
base: '/',
|
||||
})
|
Reference in New Issue
Block a user