mirror of
https://github.com/jiangrui1994/CloudSaver.git
synced 2026-01-12 16:18:45 +08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1948a888e | ||
|
|
7d8f4b32f7 | ||
|
|
0a9b053dff | ||
|
|
73440cca45 | ||
|
|
5987e1fb3e | ||
|
|
42722ca1d8 | ||
|
|
6efdea55aa | ||
|
|
f23e78e8dd | ||
|
|
26381fa6b0 | ||
|
|
eedd68d137 | ||
|
|
a04f16bfa4 | ||
|
|
93d6a1276a | ||
|
|
505b1d6c67 | ||
|
|
212ca3a879 | ||
|
|
65a35225d0 | ||
|
|
7ed0c04111 | ||
|
|
e8f70b286e | ||
|
|
e708524a41 | ||
|
|
a01dd06ef2 |
@@ -1,8 +0,0 @@
|
|||||||
# jwt密钥 用于生成token加密
|
|
||||||
JWT_SECRET=""
|
|
||||||
|
|
||||||
# 用户注册码
|
|
||||||
REGISTER_CODE='9527'
|
|
||||||
|
|
||||||
# 服务端口
|
|
||||||
PORT=8009
|
|
||||||
18
.github/workflows/docker-build-test.yml
vendored
18
.github/workflows/docker-build-test.yml
vendored
@@ -1,17 +1,16 @@
|
|||||||
name: Build and Push Multi-Arch Docker Image for Test
|
name: Build and Push Multi-Arch Docker Image for Test
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_dispatch: # 添加手动触发
|
||||||
branches:
|
|
||||||
- dev
|
|
||||||
workflow_dispatch: # 添加手动触发
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push:
|
build-and-push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write # 必须授权以推送镜像
|
packages: write # 必须授权以推送镜像
|
||||||
env:
|
env:
|
||||||
REPO_NAME: ${{ github.repository }}
|
REPO_NAME: ${{ github.repository }}
|
||||||
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
IMAGE_NAME: cloudsaver
|
||||||
steps:
|
steps:
|
||||||
- name: 检出代码
|
- name: 检出代码
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -28,6 +27,12 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: 登录到 Docker Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: 设置 QEMU 支持多架构
|
- name: 设置 QEMU 支持多架构
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
@@ -38,7 +43,8 @@ jobs:
|
|||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64 # 指定架构:x86_64 和 ARM64
|
platforms: linux/amd64,linux/arm64 # 指定架构:x86_64 和 ARM64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
ghcr.io/${{ env.LOWER_NAME }}:test
|
ghcr.io/${{ env.LOWER_NAME }}:test
|
||||||
|
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:test
|
||||||
|
|||||||
24
.github/workflows/docker-image.yml
vendored
24
.github/workflows/docker-image.yml
vendored
@@ -1,24 +1,28 @@
|
|||||||
name: Docker Image CI/CD
|
name: Docker Image CI/CD
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: [ "v*.*.*" ] # 支持标签触发(如 v1.0.0)
|
tags: ["v*.*.*"] # 支持标签触发(如 v1.0.0)
|
||||||
workflow_dispatch: # 添加手动触发
|
workflow_dispatch: # 添加手动触发
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push:
|
build-and-push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write # 必须授权以推送镜像
|
packages: write # 必须授权以推送镜像
|
||||||
env:
|
env:
|
||||||
REPO_NAME: ${{ github.repository }}
|
REPO_NAME: ${{ github.repository }}
|
||||||
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
IMAGE_NAME: cloudsaver
|
||||||
steps:
|
steps:
|
||||||
- name: 检出代码
|
- name: 检出代码
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: 设置小写镜像名称
|
- name: 设置小写镜像名称和版本
|
||||||
run: |
|
run: |
|
||||||
LOWER_NAME=$(echo "$REPO_NAME" | tr '[:upper:]' '[:lower:]')
|
LOWER_NAME=$(echo "$REPO_NAME" | tr '[:upper:]' '[:lower:]')
|
||||||
echo "LOWER_NAME=$LOWER_NAME" >> $GITHUB_ENV
|
echo "LOWER_NAME=$LOWER_NAME" >> $GITHUB_ENV
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/v}
|
||||||
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: 登录到 GitHub Container Registry
|
- name: 登录到 GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
@@ -27,6 +31,12 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: 登录到 Docker Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: 设置 QEMU 支持多架构
|
- name: 设置 QEMU 支持多架构
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
@@ -37,8 +47,10 @@ jobs:
|
|||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64 # 指定架构:x86_64 和 ARM64
|
platforms: linux/amd64,linux/arm64 # 指定架构:x86_64 和 ARM64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
ghcr.io/${{ env.LOWER_NAME }}:latest
|
ghcr.io/${{ env.LOWER_NAME }}:latest
|
||||||
ghcr.io/${{ env.LOWER_NAME }}:${{ github.sha }}
|
ghcr.io/${{ env.LOWER_NAME }}:${{ env.VERSION }}
|
||||||
|
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ env.VERSION }}
|
||||||
|
|||||||
14
Dockerfile
14
Dockerfile
@@ -26,6 +26,9 @@ RUN apk add --no-cache nginx
|
|||||||
# 设置工作目录
|
# 设置工作目录
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 创建配置和数据目录
|
||||||
|
RUN mkdir -p /app/config /app/data
|
||||||
|
|
||||||
# 复制前端构建产物到 Nginx
|
# 复制前端构建产物到 Nginx
|
||||||
COPY --from=frontend-build /app/dist /usr/share/nginx/html
|
COPY --from=frontend-build /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
@@ -38,8 +41,15 @@ COPY --from=backend-build /app /app
|
|||||||
# 安装生产环境依赖
|
# 安装生产环境依赖
|
||||||
RUN npm install --production
|
RUN npm install --production
|
||||||
|
|
||||||
|
# 设置数据卷
|
||||||
|
VOLUME ["/app/config", "/app/data"]
|
||||||
|
|
||||||
# 暴露端口
|
# 暴露端口
|
||||||
EXPOSE 8008
|
EXPOSE 8008
|
||||||
|
|
||||||
# 启动 Nginx 和后端服务
|
# 启动脚本
|
||||||
CMD ["sh", "-c", "nginx -g 'daemon off;' & npm run start && wait"]
|
COPY docker-entrypoint.sh /app/
|
||||||
|
RUN chmod +x /app/docker-entrypoint.sh
|
||||||
|
|
||||||
|
# 启动服务
|
||||||
|
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||||
|
|||||||
42
README.md
42
README.md
@@ -45,7 +45,6 @@
|
|||||||
<p>资源转存</p>
|
<p>资源转存</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
### 移动端
|
### 移动端
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
@@ -108,7 +107,7 @@ pnpm install
|
|||||||
3. 配置环境变量
|
3. 配置环境变量
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example ./backend/.env
|
cp ./backend/.env.example ./backend/.env
|
||||||
```
|
```
|
||||||
|
|
||||||
根据 `.env.example` 文件说明配置必要的环境变量。
|
根据 `.env.example` 文件说明配置必要的环境变量。
|
||||||
@@ -142,6 +141,15 @@ pnpm start
|
|||||||
|
|
||||||
### Docker 部署
|
### Docker 部署
|
||||||
|
|
||||||
|
说明:镜像源有**两个地址**供选择,下面部署命令中使用的是dockerhub托管的地址为例,github托管的地址请自行替换
|
||||||
|
|
||||||
|
- dockerhub托管:
|
||||||
|
- `jiangrui1994/cloudsaver:latest` 稳定版
|
||||||
|
- `jiangrui1994/cloudsaver:test` 测试版 (包含最新功能和bug修复,但可能不如稳定版稳定)
|
||||||
|
- github托管:
|
||||||
|
- `ghcr.io/jiangrui1994/cloudsaver:latest` 稳定版
|
||||||
|
- `ghcr.io/jiangrui1994/cloudsaver:test` 测试版 (包含最新功能和bug修复,但可能不如稳定版稳定)
|
||||||
|
|
||||||
#### 单容器部署
|
#### 单容器部署
|
||||||
|
|
||||||
稳定版:
|
稳定版:
|
||||||
@@ -150,8 +158,9 @@ pnpm start
|
|||||||
docker run -d \
|
docker run -d \
|
||||||
-p 8008:8008 \
|
-p 8008:8008 \
|
||||||
-v /your/local/path:/app/data \
|
-v /your/local/path:/app/data \
|
||||||
|
-v /your/local/path/config:/app/config \
|
||||||
--name cloud-saver \
|
--name cloud-saver \
|
||||||
ghcr.io/jiangrui1994/cloudsaver:latest
|
jiangrui1994/cloudsaver:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
测试版(包含最新功能和bug修复,但可能不如稳定版稳定):
|
测试版(包含最新功能和bug修复,但可能不如稳定版稳定):
|
||||||
@@ -160,8 +169,9 @@ docker run -d \
|
|||||||
docker run -d \
|
docker run -d \
|
||||||
-p 8008:8008 \
|
-p 8008:8008 \
|
||||||
-v /your/local/path:/app/data \
|
-v /your/local/path:/app/data \
|
||||||
|
-v /your/local/path/config:/app/config \
|
||||||
--name cloud-saver \
|
--name cloud-saver \
|
||||||
ghcr.io/jiangrui1994/cloudsaver:test
|
jiangrui1994/cloudsaver:test
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Docker Compose 部署
|
#### Docker Compose 部署
|
||||||
@@ -174,12 +184,13 @@ docker run -d \
|
|||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
cloudsaver:
|
cloudsaver:
|
||||||
image: ghcr.io/jiangrui1994/cloudsaver:latest
|
image: jiangrui1994/cloudsaver:latest
|
||||||
container_name: cloud-saver
|
container_name: cloud-saver
|
||||||
ports:
|
ports:
|
||||||
- "8008:8008"
|
- "8008:8008"
|
||||||
volumes:
|
volumes:
|
||||||
- /your/local/path:/app/data
|
- /your/local/path:/app/data
|
||||||
|
- /your/local/path/config:/app/config
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -189,15 +200,31 @@ services:
|
|||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
cloudsaver:
|
cloudsaver:
|
||||||
image: ghcr.io/jiangrui1994/cloudsaver:test
|
image: jiangrui1994/cloudsaver:test
|
||||||
container_name: cloud-saver
|
container_name: cloud-saver
|
||||||
ports:
|
ports:
|
||||||
- "8008:8008"
|
- "8008:8008"
|
||||||
volumes:
|
volumes:
|
||||||
- /your/local/path:/app/data
|
- /your/local/path:/app/data
|
||||||
|
- /your/local/path/config:/app/config
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### /app/config 目录说明
|
||||||
|
|
||||||
|
- `env` 文件:包含后端环境变量配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# JWT配置
|
||||||
|
JWT_SECRET=your_jwt_secret_here
|
||||||
|
|
||||||
|
# Telegram配置
|
||||||
|
TELEGRAM_BASE_URL=https://t.me/s
|
||||||
|
|
||||||
|
# Telegram频道配置
|
||||||
|
TELE_CHANNELS=[{"id":"xxxx","name":"xxxx资源分享"}]
|
||||||
|
```
|
||||||
|
|
||||||
运行:
|
运行:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -230,7 +257,7 @@ docker-compose up -d
|
|||||||
|
|
||||||
- ⭐ 给项目点个 Star
|
- ⭐ 给项目点个 Star
|
||||||
- 🎉 分享给更多有需要的朋友
|
- 🎉 分享给更多有需要的朋友
|
||||||
- ☕ 请作者喝杯咖啡
|
- ☕ 请作者喝瓶可乐🥤或者咖啡☕
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<div style="display: inline-block; margin: 0 20px;">
|
<div style="display: inline-block; margin: 0 20px;">
|
||||||
@@ -258,7 +285,6 @@ docker-compose up -d
|
|||||||
|
|
||||||
本项目基于 MIT 协议开源 - 查看 [LICENSE](LICENSE) 文件了解更多细节
|
本项目基于 MIT 协议开源 - 查看 [LICENSE](LICENSE) 文件了解更多细节
|
||||||
|
|
||||||
|
|
||||||
## 鸣谢
|
## 鸣谢
|
||||||
|
|
||||||
- 👨💻 感谢所有为这个项目做出贡献的开发者们!
|
- 👨💻 感谢所有为这个项目做出贡献的开发者们!
|
||||||
|
|||||||
9
backend/.env.example
Normal file
9
backend/.env.example
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# JWT配置
|
||||||
|
JWT_SECRET=your_jwt_secret_here
|
||||||
|
|
||||||
|
# Telegram配置
|
||||||
|
TELEGRAM_BASE_URL=https://t.me/s
|
||||||
|
|
||||||
|
# Telegram频道配置
|
||||||
|
TELE_CHANNELS=[{"id":"guaguale115","name":"115网盘资源分享"},{"id":"hao115","name":"115网盘资源分享频道"},{"id":"yunpanshare","name":"网盘资源收藏(夸克)"}]
|
||||||
|
|
||||||
@@ -11,86 +11,67 @@ interface Channel {
|
|||||||
interface CloudPatterns {
|
interface CloudPatterns {
|
||||||
baiduPan: RegExp;
|
baiduPan: RegExp;
|
||||||
tianyi: RegExp;
|
tianyi: RegExp;
|
||||||
weiyun: RegExp;
|
|
||||||
aliyun: RegExp;
|
aliyun: RegExp;
|
||||||
pan115: RegExp;
|
pan115: RegExp;
|
||||||
|
pan123: RegExp;
|
||||||
quark: RegExp;
|
quark: RegExp;
|
||||||
|
yidong: RegExp;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Cloud115Config {
|
|
||||||
userId: string;
|
|
||||||
cookie: string;
|
|
||||||
}
|
|
||||||
interface QuarkConfig {
|
|
||||||
userId: string;
|
|
||||||
cookie: string;
|
|
||||||
}
|
|
||||||
interface HttpProxyConfig {
|
|
||||||
host: string;
|
|
||||||
port: string;
|
|
||||||
}
|
|
||||||
interface Config {
|
interface Config {
|
||||||
jwtSecret: string;
|
jwtSecret: string;
|
||||||
registerCode: string;
|
telegram: {
|
||||||
rss: {
|
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
channels: Channel[];
|
channels: Channel[];
|
||||||
};
|
};
|
||||||
telegram: {
|
|
||||||
baseUrl: string;
|
|
||||||
};
|
|
||||||
httpProxy: HttpProxyConfig;
|
|
||||||
cloudPatterns: CloudPatterns;
|
cloudPatterns: CloudPatterns;
|
||||||
cloud115: Cloud115Config;
|
|
||||||
quark: QuarkConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从环境变量读取频道配置
|
||||||
|
const getTeleChannels = (): Channel[] => {
|
||||||
|
try {
|
||||||
|
const channelsStr = process.env.TELE_CHANNELS;
|
||||||
|
if (channelsStr) {
|
||||||
|
return JSON.parse(channelsStr);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("无法解析 TELE_CHANNELS 环境变量,使用默认配置");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认配置
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "guaguale115",
|
||||||
|
name: "115网盘资源分享",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "hao115",
|
||||||
|
name: "115网盘资源分享频道",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "yunpanshare",
|
||||||
|
name: "网盘资源收藏(夸克)",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
export const config: Config = {
|
export const config: Config = {
|
||||||
jwtSecret: process.env.JWT_SECRET || "uV7Y$k92#LkF^q1b!",
|
jwtSecret: process.env.JWT_SECRET || "uV7Y$k92#LkF^q1b!",
|
||||||
rss: {
|
|
||||||
baseUrl: process.env.RSS_BASE_URL || "https://rsshub.rssforever.com/telegram/channel",
|
|
||||||
channels: [
|
|
||||||
{
|
|
||||||
id: "guaguale115",
|
|
||||||
name: "115网盘资源分享",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "hao115",
|
|
||||||
name: "115网盘资源分享频道",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "yunpanshare",
|
|
||||||
name: "网盘资源收藏(夸克)",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
registerCode: process.env.REGISTER_CODE || "9527",
|
|
||||||
|
|
||||||
telegram: {
|
telegram: {
|
||||||
baseUrl: process.env.TELEGRAM_BASE_URL || "https://t.me/s",
|
baseUrl: process.env.TELEGRAM_BASE_URL || "https://t.me/s",
|
||||||
},
|
channels: getTeleChannels(),
|
||||||
|
|
||||||
httpProxy: {
|
|
||||||
host: process.env.HTTP_PROXY_HOST || "",
|
|
||||||
port: process.env.HTTP_PROXY_PORT || "",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
cloudPatterns: {
|
cloudPatterns: {
|
||||||
baiduPan: /https?:\/\/(?:pan|yun)\.baidu\.com\/[^\s<>"]+/g,
|
baiduPan: /https?:\/\/(?:pan|yun)\.baidu\.com\/[^\s<>"]+/g,
|
||||||
tianyi: /https?:\/\/cloud\.189\.cn\/[^\s<>"]+/g,
|
tianyi: /https?:\/\/cloud\.189\.cn\/[^\s<>"]+/g,
|
||||||
weiyun: /https?:\/\/share\.weiyun\.com\/[^\s<>"]+/g,
|
aliyun: /https?:\/\/\w+\.(?:alipan|aliyundrive)\.com\/[^\s<>"]+/g,
|
||||||
aliyun: /https?:\/\/\w+\.aliyundrive\.com\/[^\s<>"]+/g,
|
// pan115有两个域名 115.com 和 anxia.com 和 115cdn.com
|
||||||
// pan115有两个域名 115.com 和 anxia.com
|
|
||||||
pan115: /https?:\/\/(?:115|anxia|115cdn)\.com\/s\/[^\s<>"]+/g,
|
pan115: /https?:\/\/(?:115|anxia|115cdn)\.com\/s\/[^\s<>"]+/g,
|
||||||
|
// 修改为匹配所有以123开头的域名
|
||||||
|
pan123: /https?:\/\/(?:www\.)?123[^\/\s<>"]+\.com\/s\/[^\s<>"]+/g,
|
||||||
quark: /https?:\/\/pan\.quark\.cn\/[^\s<>"]+/g,
|
quark: /https?:\/\/pan\.quark\.cn\/[^\s<>"]+/g,
|
||||||
},
|
yidong: /https?:\/\/caiyun\.139\.com\/[^\s<>"]+/g,
|
||||||
|
|
||||||
cloud115: {
|
|
||||||
userId: "",
|
|
||||||
cookie: process.env.CLOUD115_COOKIE || "",
|
|
||||||
},
|
|
||||||
quark: {
|
|
||||||
userId: process.env.QUARK_USER_ID || "",
|
|
||||||
cookie: process.env.QUARK_COOKIE || "",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { AxiosHeaders, AxiosInstance } from "axios"; // 导入 AxiosHeaders
|
import { AxiosHeaders, AxiosInstance } from "axios"; // 导入 AxiosHeaders
|
||||||
import { createAxiosInstance } from "../utils/axiosInstance";
|
import { createAxiosInstance } from "../utils/axiosInstance";
|
||||||
import { Logger } from "../utils/logger";
|
import { Logger } from "../utils/logger";
|
||||||
import { config } from "../config/index";
|
|
||||||
import { ShareInfoResponse } from "../types/cloud115";
|
import { ShareInfoResponse } from "../types/cloud115";
|
||||||
|
|
||||||
interface Cloud115ListItem {
|
interface Cloud115ListItem {
|
||||||
@@ -128,7 +127,6 @@ export class Cloud115Service {
|
|||||||
}): Promise<{ message: string; data: unknown }> {
|
}): Promise<{ message: string; data: unknown }> {
|
||||||
const param = new URLSearchParams({
|
const param = new URLSearchParams({
|
||||||
cid: params.cid,
|
cid: params.cid,
|
||||||
user_id: config.cloud115.userId,
|
|
||||||
share_code: params.shareCode,
|
share_code: params.shareCode,
|
||||||
receive_code: params.receiveCode,
|
receive_code: params.receiveCode,
|
||||||
file_id: params.fileId,
|
file_id: params.fileId,
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ export class Searcher {
|
|||||||
const allResults: any[] = [];
|
const allResults: any[] = [];
|
||||||
|
|
||||||
const channelList: any[] = channelId
|
const channelList: any[] = channelId
|
||||||
? config.rss.channels.filter((channel: any) => channel.id === channelId)
|
? config.telegram.channels.filter((channel: any) => channel.id === channelId)
|
||||||
: config.rss.channels;
|
: config.telegram.channels;
|
||||||
|
|
||||||
// 使用Promise.all进行并行请求
|
// 使用Promise.all进行并行请求
|
||||||
const searchPromises = channelList.map(async (channel) => {
|
const searchPromises = channelList.map(async (channel) => {
|
||||||
|
|||||||
13
docker-entrypoint.sh
Normal file
13
docker-entrypoint.sh
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# 如果配置目录下没有 env 文件,则复制示例文件
|
||||||
|
if [ ! -f /app/config/env ]; then
|
||||||
|
cp /app/.env.example /app/config/env
|
||||||
|
echo "已创建默认配置文件 /app/config/env,请根据需要修改配置"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建配置文件软链接
|
||||||
|
ln -sf /app/config/env /app/.env
|
||||||
|
|
||||||
|
# 启动 Nginx 和后端服务
|
||||||
|
nginx -g 'daemon off;' & npm run start
|
||||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cloud-disk-web",
|
"name": "cloud-disk-web",
|
||||||
"version": "0.1.0",
|
"version": "0.2.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cloud-disk-web",
|
"name": "cloud-disk-web",
|
||||||
"version": "0.1.0",
|
"version": "0.2.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"element-plus": "^2.6.1",
|
"element-plus": "^2.6.1",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "cloud-saver-web",
|
"name": "cloud-saver-web",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.2.1",
|
"version": "0.2.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
<style>
|
<style>
|
||||||
#app {
|
#app {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
:root {
|
:root {
|
||||||
--theme-color: #3e3e3e;
|
--theme-color: #3e3e3e;
|
||||||
@@ -34,6 +38,13 @@ body {
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
/* 移动端全局样式 */
|
/* 移动端全局样式 */
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
#app {
|
#app {
|
||||||
|
|||||||
@@ -12,7 +12,11 @@
|
|||||||
<div class="detail-cover">
|
<div class="detail-cover">
|
||||||
<el-image
|
<el-image
|
||||||
class="cover-image"
|
class="cover-image"
|
||||||
:src="`/tele-images/?url=${encodeURIComponent(currentResource.image as string)}`"
|
:src="
|
||||||
|
userStore.imagesSource === 'proxy'
|
||||||
|
? `/tele-images/?url=${encodeURIComponent(currentResource.image as string)}`
|
||||||
|
: currentResource.image
|
||||||
|
"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
/>
|
/>
|
||||||
<el-tag
|
<el-tag
|
||||||
@@ -56,14 +60,25 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<div v-for="group in store.resources" :key="group.id" class="resource-group">
|
<div v-for="group in store.resources" :key="group.id" class="resource-group">
|
||||||
<div class="group-header">
|
<div class="group-header" @click="group.displayList = !group.displayList">
|
||||||
<el-link
|
<el-link
|
||||||
class="group-title"
|
class="group-title"
|
||||||
:href="`https://t.me/s/${group.id}`"
|
:href="`https://t.me/s/${group.id}`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
:underline="false"
|
:underline="false"
|
||||||
|
@click.stop
|
||||||
>
|
>
|
||||||
<el-image :src="group.channelInfo.channelLogo" class="channel-logo" fit="cover" lazy />
|
<el-image
|
||||||
|
:src="
|
||||||
|
userStore.imagesSource === 'proxy'
|
||||||
|
? `/tele-images/?url=${encodeURIComponent(group.channelInfo.channelLogo)}`
|
||||||
|
: group.channelInfo.channelLogo
|
||||||
|
"
|
||||||
|
class="channel-logo"
|
||||||
|
scroll-container="#pc-resources-content"
|
||||||
|
fit="cover"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
<span>{{ group.channelInfo.name }}</span>
|
<span>{{ group.channelInfo.name }}</span>
|
||||||
<span class="item-count">({{ group.list.length }})</span>
|
<span class="item-count">({{ group.list.length }})</span>
|
||||||
</el-link>
|
</el-link>
|
||||||
@@ -88,10 +103,14 @@
|
|||||||
<div class="card-wrapper">
|
<div class="card-wrapper">
|
||||||
<div class="card-cover">
|
<div class="card-cover">
|
||||||
<el-image
|
<el-image
|
||||||
|
loading="lazy"
|
||||||
class="cover-image"
|
class="cover-image"
|
||||||
:src="`/tele-images/?url=${encodeURIComponent(resource.image as string)}`"
|
:src="
|
||||||
|
userStore.imagesSource === 'proxy'
|
||||||
|
? `/tele-images/?url=${encodeURIComponent(resource.image as string)}`
|
||||||
|
: resource.image
|
||||||
|
"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
lazy
|
|
||||||
:alt="resource.title"
|
:alt="resource.title"
|
||||||
@click="showResourceDetail(resource)"
|
@click="showResourceDetail(resource)"
|
||||||
/>
|
/>
|
||||||
@@ -160,8 +179,11 @@ import { useResourceStore } from "@/stores/resource";
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import type { ResourceItem, TagColor } from "@/types";
|
import type { ResourceItem, TagColor } from "@/types";
|
||||||
import { ArrowDown, Plus } from "@element-plus/icons-vue";
|
import { ArrowDown, Plus } from "@element-plus/icons-vue";
|
||||||
|
import { useUserSettingStore } from "@/stores/userSetting";
|
||||||
|
|
||||||
|
const userStore = useUserSettingStore();
|
||||||
const store = useResourceStore();
|
const store = useResourceStore();
|
||||||
|
|
||||||
const showDetail = ref(false);
|
const showDetail = ref(false);
|
||||||
const currentResource = ref<ResourceItem | null>(null);
|
const currentResource = ref<ResourceItem | null>(null);
|
||||||
|
|
||||||
@@ -216,6 +238,15 @@ const handleLoadMore = (channelId: string) => {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 12px 20px;
|
padding: 12px 20px;
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background: var(--theme-card-bg);
|
||||||
|
backdrop-filter: var(--theme-blur);
|
||||||
|
-webkit-backdrop-filter: var(--theme-blur);
|
||||||
|
z-index: 10;
|
||||||
|
border-radius: var(--theme-radius) var(--theme-radius) 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
.group-title {
|
.group-title {
|
||||||
@include flex-center;
|
@include flex-center;
|
||||||
@@ -230,6 +261,7 @@ const handleLoadMore = (channelId: string) => {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: var(--theme-shadow-sm);
|
box-shadow: var(--theme-shadow-sm);
|
||||||
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-count {
|
.item-count {
|
||||||
|
|||||||
@@ -15,10 +15,18 @@
|
|||||||
<el-image
|
<el-image
|
||||||
v-if="row.image"
|
v-if="row.image"
|
||||||
class="table-item-image"
|
class="table-item-image"
|
||||||
:src="`/tele-images/?url=${encodeURIComponent(row.image as string)}`"
|
:src="
|
||||||
|
userStore.imagesSource === 'proxy'
|
||||||
|
? `/tele-images/?url=${encodeURIComponent(row.image as string)}`
|
||||||
|
: row.image
|
||||||
|
"
|
||||||
hide-on-click-modal
|
hide-on-click-modal
|
||||||
:preview-src-list="[
|
:preview-src-list="[
|
||||||
`${location.origin}/tele-images/?url=${encodeURIComponent(row.image as string)}`,
|
`${location.origin}${
|
||||||
|
userStore.imagesSource === 'proxy'
|
||||||
|
? '/tele-images/?url=' + encodeURIComponent(row.image as string)
|
||||||
|
: row.image
|
||||||
|
}`,
|
||||||
]"
|
]"
|
||||||
:zoom-rate="1.2"
|
:zoom-rate="1.2"
|
||||||
:max-scale="7"
|
:max-scale="7"
|
||||||
@@ -84,7 +92,16 @@
|
|||||||
<el-table-column label="来源" prop="channel">
|
<el-table-column label="来源" prop="channel">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="group-header">
|
<div class="group-header">
|
||||||
<el-image :src="row.channelInfo.channelLogo" class="channel-logo" fit="cover" lazy />
|
<el-image
|
||||||
|
:src="
|
||||||
|
userStore.imagesSource === 'proxy'
|
||||||
|
? `/tele-images/?url=${encodeURIComponent(row.channelInfo.channelLogo as string)}`
|
||||||
|
: row.channelInfo.channelLogo
|
||||||
|
"
|
||||||
|
class="channel-logo"
|
||||||
|
fit="cover"
|
||||||
|
lazy
|
||||||
|
/>
|
||||||
<span>{{ row.channelInfo.name }}</span>
|
<span>{{ row.channelInfo.name }}</span>
|
||||||
<span class="item-count">({{ row.list.length }})</span>
|
<span class="item-count">({{ row.list.length }})</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,6 +114,8 @@
|
|||||||
import { useResourceStore } from "@/stores/resource";
|
import { useResourceStore } from "@/stores/resource";
|
||||||
import type { Resource, TagColor } from "@/types";
|
import type { Resource, TagColor } from "@/types";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import { useUserSettingStore } from "@/stores/userSetting";
|
||||||
|
const userStore = useUserSettingStore();
|
||||||
|
|
||||||
const store = useResourceStore();
|
const store = useResourceStore();
|
||||||
|
|
||||||
|
|||||||
@@ -83,10 +83,16 @@ watch(
|
|||||||
keyword.value = newKeyword;
|
keyword.value = newKeyword;
|
||||||
handleSearch();
|
handleSearch();
|
||||||
} else {
|
} else {
|
||||||
keyword.value = "";
|
keyword.value = resourcStore.keyword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
watch(
|
||||||
|
() => resourcStore.keyword,
|
||||||
|
(newKeyword) => {
|
||||||
|
keyword.value = newKeyword;
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -26,8 +26,10 @@
|
|||||||
<!-- 描述 - 添加展开收起功能 -->
|
<!-- 描述 - 添加展开收起功能 -->
|
||||||
<div
|
<div
|
||||||
class="info__desc"
|
class="info__desc"
|
||||||
:class="{ 'is-expanded': expandedItems[item.id] }"
|
:class="{
|
||||||
@click="toggleExpand(item.id)"
|
'is-expanded': expandedItems[(item.messageId || '') + (item.channelId || '')],
|
||||||
|
}"
|
||||||
|
@click="toggleExpand((item.messageId || '') + (item.channelId || ''))"
|
||||||
v-html="item.content"
|
v-html="item.content"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -100,12 +100,12 @@ export const useResourceStore = defineStore("resource", {
|
|||||||
pan115: "danger",
|
pan115: "danger",
|
||||||
quark: "success",
|
quark: "success",
|
||||||
},
|
},
|
||||||
|
keyword: "",
|
||||||
resources: lastResource.list,
|
resources: lastResource.list,
|
||||||
lastUpdateTime: lastResource.lastUpdateTime || "",
|
lastUpdateTime: lastResource.lastUpdateTime || "",
|
||||||
shareInfo: {} as ShareInfoResponse,
|
shareInfo: {} as ShareInfoResponse,
|
||||||
resourceSelect: [] as ShareInfo[],
|
resourceSelect: [] as ShareInfo[],
|
||||||
loading: false,
|
loading: false,
|
||||||
lastKeyWord: "",
|
|
||||||
backupPlan: false,
|
backupPlan: false,
|
||||||
loadTree: false,
|
loadTree: false,
|
||||||
}),
|
}),
|
||||||
@@ -123,36 +123,45 @@ export const useResourceStore = defineStore("resource", {
|
|||||||
if (isLoadMore) {
|
if (isLoadMore) {
|
||||||
const list = this.resources.find((x) => x.id === channelId)?.list || [];
|
const list = this.resources.find((x) => x.id === channelId)?.list || [];
|
||||||
lastMessageId = list[list.length - 1].messageId || "";
|
lastMessageId = list[list.length - 1].messageId || "";
|
||||||
|
if (list[list.length - 1].isLastMessage) {
|
||||||
|
ElMessage.warning("没有更多了~");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!lastMessageId) {
|
if (!lastMessageId) {
|
||||||
ElMessage.error("当次搜索源不支持加载更多");
|
ElMessage.error("当次搜索源不支持加载更多");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
keyword = this.lastKeyWord;
|
keyword = this.keyword;
|
||||||
}
|
}
|
||||||
let { data = [] } = await resourceApi.search(keyword || "", channelId, lastMessageId);
|
let { data = [] } = await resourceApi.search(keyword || "", channelId, lastMessageId);
|
||||||
|
this.keyword = keyword || "";
|
||||||
data = data.filter((item) => item.list.length > 0);
|
data = data.filter((item) => item.list.length > 0);
|
||||||
this.lastKeyWord = keyword || "";
|
|
||||||
if (isLoadMore) {
|
if (isLoadMore) {
|
||||||
const findedIndex = this.resources.findIndex((item) => item.id === data[0]?.id);
|
const findedIndex = this.resources.findIndex((item) => item.id === data[0]?.id);
|
||||||
if (findedIndex !== -1) {
|
if (findedIndex !== -1) {
|
||||||
this.resources[findedIndex].list.push(...data[0].list);
|
this.resources[findedIndex].list.push(...data[0].list);
|
||||||
}
|
}
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
|
const list = this.resources.find((item) => item.id === channelId)?.list;
|
||||||
|
list && list[list.length - 1] && (list[list.length - 1]!.isLastMessage = true);
|
||||||
ElMessage.warning("没有更多了~");
|
ElMessage.warning("没有更多了~");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.resources = data.map((item) => ({ ...item, displayList: true }));
|
this.resources = data.map((item, index) => ({ ...item, displayList: index === 0 }));
|
||||||
|
if (!keyword) {
|
||||||
|
// 获取当前时间字符串 用于存储到本地
|
||||||
|
this.lastUpdateTime = new Date().toLocaleString();
|
||||||
|
localStorage.setItem(
|
||||||
|
"last_resource_list",
|
||||||
|
JSON.stringify({ list: this.resources, lastUpdateTime: this.lastUpdateTime })
|
||||||
|
);
|
||||||
|
}
|
||||||
if (this.resources.length === 0) {
|
if (this.resources.length === 0) {
|
||||||
ElMessage.warning("未搜索到相关资源");
|
ElMessage.warning("未搜索到相关资源");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 获取当前时间字符串 用于存储到本地
|
|
||||||
this.lastUpdateTime = new Date().toLocaleString();
|
|
||||||
localStorage.setItem(
|
|
||||||
"last_resource_list",
|
|
||||||
JSON.stringify({ list: this.resources, lastUpdateTime: this.lastUpdateTime })
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
this.handleError("搜索失败,请重试", null);
|
this.handleError("搜索失败,请重试", null);
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ export const useUserSettingStore = defineStore("user", {
|
|||||||
cloud115Cookie: "",
|
cloud115Cookie: "",
|
||||||
quarkCookie: "",
|
quarkCookie: "",
|
||||||
},
|
},
|
||||||
displayStyle: "card",
|
displayStyle: (localStorage.getItem("display_style") as "table" | "card") || "card",
|
||||||
|
imagesSource: (localStorage.getItem("images_source") as "proxy" | "local") || "proxy",
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@@ -41,7 +42,14 @@ export const useUserSettingStore = defineStore("user", {
|
|||||||
|
|
||||||
setDisplayStyle(style: "table" | "card") {
|
setDisplayStyle(style: "table" | "card") {
|
||||||
this.displayStyle = style;
|
this.displayStyle = style;
|
||||||
ElMessage.success(`切换成功,当前为${style}模式`);
|
localStorage.setItem("display_style", style);
|
||||||
|
ElMessage.success(`切换成功,当前为${style === "table" ? "列表" : "卡片"}模式`);
|
||||||
|
},
|
||||||
|
|
||||||
|
setImagesSource(source: "proxy" | "local") {
|
||||||
|
this.imagesSource = source;
|
||||||
|
localStorage.setItem("images_source", source);
|
||||||
|
ElMessage.success(`切换成功,图片模式当前为${source === "proxy" ? "代理" : "直连"}模式`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface ResourceItem {
|
|||||||
pubDate: string;
|
pubDate: string;
|
||||||
cloudType: string;
|
cloudType: string;
|
||||||
messageId?: string;
|
messageId?: string;
|
||||||
|
isLastMessage?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Resource {
|
export interface Resource {
|
||||||
|
|||||||
@@ -15,4 +15,5 @@ export interface UserSettingStore {
|
|||||||
globalSetting: GlobalSettingAttributes | null;
|
globalSetting: GlobalSettingAttributes | null;
|
||||||
userSettings: UserSettingAttributes;
|
userSettings: UserSettingAttributes;
|
||||||
displayStyle: "table" | "card";
|
displayStyle: "table" | "card";
|
||||||
|
imagesSource: "proxy" | "local";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header__right">
|
<div class="header__right">
|
||||||
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
:content="
|
||||||
|
userStore.imagesSource === 'local' ? '图片切换到代理模式' : '图片切换到直连模式'
|
||||||
|
"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
class="view-toggle"
|
||||||
|
@click="
|
||||||
|
userStore.setImagesSource(userStore.imagesSource === 'proxy' ? 'local' : 'proxy')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<el-icon>
|
||||||
|
<component :is="userStore.imagesSource === 'proxy' ? 'Guide' : 'Location'" />
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
effect="dark"
|
effect="dark"
|
||||||
:content="userStore.displayStyle === 'card' ? '切换到列表视图' : '切换到卡片视图'"
|
:content="userStore.displayStyle === 'card' ? '切换到列表视图' : '切换到卡片视图'"
|
||||||
@@ -32,7 +51,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 资源列表 -->
|
<!-- 资源列表 -->
|
||||||
<div ref="contentRef" class="pc-resources__content">
|
<div id="pc-resources-content" ref="contentRef" class="pc-resources__content">
|
||||||
<component
|
<component
|
||||||
:is="userStore.displayStyle === 'table' ? ResourceTable : ResourceCard"
|
:is="userStore.displayStyle === 'table' ? ResourceTable : ResourceCard"
|
||||||
v-if="resourceStore.resources.length > 0"
|
v-if="resourceStore.resources.length > 0"
|
||||||
@@ -142,7 +161,7 @@ import ResourceSelect from "@/components/Home/ResourceSelect.vue";
|
|||||||
import ResourceTable from "@/components/Home/ResourceTable.vue";
|
import ResourceTable from "@/components/Home/ResourceTable.vue";
|
||||||
import { formattedFileSize } from "@/utils/index";
|
import { formattedFileSize } from "@/utils/index";
|
||||||
import type { ResourceItem, TagColor } from "@/types";
|
import type { ResourceItem, TagColor } from "@/types";
|
||||||
|
import { onMounted, onBeforeUnmount } from "vue";
|
||||||
import ResourceCard from "@/components/Home/ResourceCard.vue";
|
import ResourceCard from "@/components/Home/ResourceCard.vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
@@ -212,8 +231,20 @@ const handleLoadMore = (channelId: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const searchMovieforTag = (tag: string) => {
|
const searchMovieforTag = (tag: string) => {
|
||||||
router.push({ path: "/", query: { keyword: tag } });
|
router.push({ path: "/resource", query: { keyword: tag } });
|
||||||
};
|
};
|
||||||
|
// 页面进入 设置缓存的数据源
|
||||||
|
onMounted(() => {
|
||||||
|
const lastResourceList = localStorage.getItem("last_resource_list");
|
||||||
|
if (lastResourceList) {
|
||||||
|
resourceStore.resources = JSON.parse(lastResourceList).list;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 页面销毁 清除搜索词
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
resourceStore.keyword = "";
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -81,10 +81,16 @@ watch(
|
|||||||
searchForm.value.keyword = keyword;
|
searchForm.value.keyword = keyword;
|
||||||
handleSearch();
|
handleSearch();
|
||||||
} else {
|
} else {
|
||||||
searchForm.value.keyword = "";
|
searchForm.value.keyword = resourceStore.keyword;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
watch(
|
||||||
|
() => resourceStore.keyword,
|
||||||
|
(newKeyword) => {
|
||||||
|
searchForm.value.keyword = newKeyword;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 方法定义
|
// 方法定义
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
@@ -164,8 +170,6 @@ const handleLogout = () => {
|
|||||||
padding-bottom: 100px; // tabbar高度 + 底部安全区域
|
padding-bottom: 100px; // tabbar高度 + 底部安全区域
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载状态
|
// 加载状态
|
||||||
|
|||||||
@@ -121,7 +121,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, onMounted, onUnmounted, computed } from "vue";
|
import { ref, watch, onMounted, onUnmounted, computed, onBeforeUnmount } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { showToast } from "vant";
|
import { showToast } from "vant";
|
||||||
import { useResourceStore } from "@/stores/resource";
|
import { useResourceStore } from "@/stores/resource";
|
||||||
@@ -223,14 +223,14 @@ const searchMovieforTag = (tag: string) => {
|
|||||||
// 使用节流包装加载更多函数
|
// 使用节流包装加载更多函数
|
||||||
const throttledLoadMore = throttle((channelId: string) => {
|
const throttledLoadMore = throttle((channelId: string) => {
|
||||||
resourceStore.searchResources("", true, channelId);
|
resourceStore.searchResources("", true, channelId);
|
||||||
}, 200);
|
}, 2000);
|
||||||
|
|
||||||
// 滚动加载
|
// 滚动加载
|
||||||
const doScroll = () => {
|
const doScroll = () => {
|
||||||
const appElement = document.querySelector("#app") as HTMLElement;
|
const appElement = document.querySelector("#app") as HTMLElement;
|
||||||
if (appElement) {
|
if (appElement) {
|
||||||
const { scrollHeight, scrollTop, clientHeight } = appElement;
|
const { scrollHeight, scrollTop, clientHeight } = appElement;
|
||||||
if (scrollHeight - (clientHeight + scrollTop) <= 200) {
|
if (scrollHeight - (clientHeight + scrollTop) <= 1) {
|
||||||
throttledLoadMore(currentTab.value);
|
throttledLoadMore(currentTab.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,6 +258,18 @@ watch(currentTab, () => {
|
|||||||
appElement.scrollTo(0, 0);
|
appElement.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// 页面进入 设置缓存的数据源
|
||||||
|
onMounted(() => {
|
||||||
|
const lastResourceList = localStorage.getItem("last_resource_list");
|
||||||
|
if (lastResourceList) {
|
||||||
|
resourceStore.resources = JSON.parse(lastResourceList).list;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 页面销毁 清除搜索词
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
resourceStore.keyword = "";
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cloud-saver",
|
"name": "cloud-saver",
|
||||||
"version": "0.2.0",
|
"version": "0.2.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cloud-saver",
|
"name": "cloud-saver",
|
||||||
"version": "0.2.0",
|
"version": "0.2.2",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"frontend",
|
"frontend",
|
||||||
"backend"
|
"backend"
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
},
|
},
|
||||||
"frontend": {
|
"frontend": {
|
||||||
"name": "cloud-saver-web",
|
"name": "cloud-saver-web",
|
||||||
"version": "0.2.0",
|
"version": "0.2.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cloud-saver",
|
"name": "cloud-saver",
|
||||||
"version": "0.2.1",
|
"version": "0.2.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"frontend",
|
"frontend",
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
"build:frontend": "cd frontend && npm run build",
|
"build:frontend": "cd frontend && npm run build",
|
||||||
"build:backend": "cd backend && npm run build",
|
"build:backend": "cd backend && npm run build",
|
||||||
"clean": "rimraf **/node_modules **/dist",
|
"clean": "rimraf **/node_modules **/dist",
|
||||||
|
"version:patch": "npm version patch -w frontend && npm version patch",
|
||||||
|
"version:minor": "npm version minor -w frontend && npm version minor",
|
||||||
|
"version:major": "npm version major -w frontend && npm version major",
|
||||||
"format": "prettier --write \"**/*.{js,ts,vue,json,css,scss}\"",
|
"format": "prettier --write \"**/*.{js,ts,vue,json,css,scss}\"",
|
||||||
"format:check": "prettier --check \"**/*.{js,ts,vue,json,css,scss}\"",
|
"format:check": "prettier --check \"**/*.{js,ts,vue,json,css,scss}\"",
|
||||||
"format:all": "npm run format && npm run lint:fix",
|
"format:all": "npm run format && npm run lint:fix",
|
||||||
|
|||||||
Reference in New Issue
Block a user