first commit

This commit is contained in:
Snowz 2025-04-17 00:03:17 +08:00
commit 349a4538b1
19 changed files with 4730 additions and 0 deletions

24
.eslintrc.js Normal file
View 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
View File

@ -0,0 +1,9 @@
{
"semi": false,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "es5",
"tabWidth": 2,
"useTabs": false,
"endOfLine": "auto"
}

21
LICENSE Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

32
package.json Normal file
View 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
View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

51
src/App.vue Normal file
View 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">
&copy; {{ 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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: '/',
})