diff --git a/.gitignore b/.gitignore index 0764fe6..6f129d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store .pw-user-data + node_modules/ dist/ npm-debug.log* diff --git a/README.md b/README.md index 3d3790b..3ef91c0 100644 --- a/README.md +++ b/README.md @@ -18,29 +18,35 @@ - 前端分页加载,避免一次加载过多图片 --- -获取的空数据 -```json - -{ - "updatedAt": "2026-03-10T18:00:00Z", - "name": "七十二家房客", - "items": [] -} +# 运行&安装 +## 一、后端 +安装后端依赖 +```shell +npm i ``` -# 安装 +开启后端服务 +```shell +node server.js +``` +如需更新数据: +```shell +node capture.js +``` + +## 二、前端 ## 1 安装前端依赖 - ``` -npm install +npm i +``` + +运行前端服务: +```shell +npm run dev ``` -安装播放器依赖: -``` -npm install axios hls.js -``` --- diff --git a/backServer/data/2024.json b/backServer/data/2024.json index c03e65e..712d4ea 100644 --- a/backServer/data/2024.json +++ b/backServer/data/2024.json @@ -1,6 +1,6 @@ { "year": 2024, - "updatedAt": "2026-03-10T20:58:50.560Z", + "updatedAt": "2026-03-10T21:35:04.482Z", "items": [ { "id": "7f012566d7827b5046de9f92a4d7e159", diff --git a/backServer/data/2025.json b/backServer/data/2025.json index 725cecd..ced23aa 100644 --- a/backServer/data/2025.json +++ b/backServer/data/2025.json @@ -1,6 +1,6 @@ { "year": 2025, - "updatedAt": "2026-03-10T21:02:47.178Z", + "updatedAt": "2026-03-10T21:34:22.356Z", "items": [ { "id": "b2b4f80b846360647d13e297c029be8d", diff --git a/backServer/data/2026.json b/backServer/data/2026.json index ecd38a9..4fd3175 100644 --- a/backServer/data/2026.json +++ b/backServer/data/2026.json @@ -1,6 +1,6 @@ { "year": 2026, - "updatedAt": "2026-03-10T21:02:47.172Z", + "updatedAt": "2026-03-10T21:33:31.594Z", "items": [ { "id": "5abbc88ea530b5db6438a4e584e80281", diff --git a/backServer/data/capture-status.json b/backServer/data/capture-status.json index 3d17d91..19943a4 100644 --- a/backServer/data/capture-status.json +++ b/backServer/data/capture-status.json @@ -1,8 +1,5 @@ { - "running": true, - "lastMessage": "已采集第 1 页", - "updatedAt": "2026-03-10T21:11:58.820Z", - "currentPage": "1", - "pageItems": 0, - "addedCount": 0 + "running": false, + "lastMessage": "自动采集完成", + "updatedAt": "2026-03-10T21:35:26.989Z" } \ No newline at end of file diff --git a/backServer/ku b/backServer/ku deleted file mode 100644 index bd1bfb3..0000000 --- a/backServer/ku +++ /dev/null @@ -1 +0,0 @@ -express cors axios diff --git a/backServer/server.js b/backServer/server.js index 7a61e7d..3139cf2 100644 --- a/backServer/server.js +++ b/backServer/server.js @@ -13,25 +13,79 @@ app.use(express.json()) ensureDataFiles() -app.get('/api/qishier/all', (_req, res) => { - try { - const cache = readAllYearsData() +let memoryCache = { + updatedAt: null, + name: '七十二家房客', + coverUrl: '', + displayType: 0, + years: [], + items: [] +} - res.json({ - updatedAt: cache.updatedAt, - name: cache.name || '七十二家房客', - coverUrl: cache.coverUrl || '', - displayType: cache.displayType || 0, - total: cache.items?.length || 0, - years: cache.years || [], - list: cache.items || [] - }) - } catch (error) { - res.status(500).json({ - message: '读取缓存失败', - error: error.message - }) +function toSimpleItem(item) { + return { + id: item.id, + title: item.title, + coverUrl: item.coverUrl, + releasedAt: item.releasedAt, + timeLength: item.timeLength, + videoUrl: item.videoUrl } +} + +function refreshMemoryCache() { + const cache = readAllYearsData() + memoryCache = { + updatedAt: cache.updatedAt, + name: cache.name || '七十二家房客', + coverUrl: cache.coverUrl || '', + displayType: cache.displayType || 0, + years: cache.years || [], + items: (cache.items || []).map(toSimpleItem) + } + + console.log( + '[server] 内存缓存已刷新:', + 'total=', memoryCache.items.length, + 'years=', memoryCache.years.length + ) +} + +refreshMemoryCache() + +app.get('/api/qishier/all', (_req, res) => { + res.json({ + updatedAt: memoryCache.updatedAt, + name: memoryCache.name, + coverUrl: memoryCache.coverUrl, + displayType: memoryCache.displayType, + total: memoryCache.items.length, + years: memoryCache.years, + list: memoryCache.items + }) +}) + +app.get('/api/qishier/list', (req, res) => { + const page = Math.max(1, Number(req.query.page || 1)) + const pageSize = Math.max(1, Math.min(100, Number(req.query.pageSize || 50))) + + const total = memoryCache.items.length + const start = (page - 1) * pageSize + const end = start + pageSize + const list = memoryCache.items.slice(start, end) + + res.json({ + updatedAt: memoryCache.updatedAt, + name: memoryCache.name, + coverUrl: memoryCache.coverUrl, + displayType: memoryCache.displayType, + total, + page, + pageSize, + hasMore: end < total, + years: memoryCache.years, + list + }) }) app.get('/api/qishier/year/:year', (req, res) => { @@ -46,7 +100,7 @@ app.get('/api/qishier/year/:year', (req, res) => { displayType: 0, total: data.items?.length || 0, years: [year], - list: data.items || [] + list: (data.items || []).map(toSimpleItem) }) } catch (error) { res.status(500).json({ @@ -56,6 +110,22 @@ app.get('/api/qishier/year/:year', (req, res) => { } }) +app.post('/api/qishier/refresh', (_req, res) => { + try { + refreshMemoryCache() + res.json({ + message: '缓存已刷新', + total: memoryCache.items.length, + updatedAt: memoryCache.updatedAt + }) + } catch (error) { + res.status(500).json({ + message: '刷新缓存失败', + error: error.message + }) + } +}) + app.get('/api/qishier/status', (_req, res) => { try { const status = readStatus() @@ -68,6 +138,6 @@ app.get('/api/qishier/status', (_req, res) => { } }) -app.listen(3000, () => { - console.log('Node 服务已启动: http://localhost:3000') +app.listen(23822, () => { + console.log('Node 服务已启动: http://127.0.0.1:23822') }) diff --git a/dist.zip b/dist.zip new file mode 100644 index 0000000..e50cfd7 Binary files /dev/null and b/dist.zip differ diff --git a/package.json b/package.json index 5a5f9e0..d1538bf 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "cors": "^2.8.6", "express": "^5.2.1", "hls.js": "^1.6.15", + "plyr": "^3.8.4", "vue": "^3.5.29" }, "devDependencies": { diff --git a/src/api/qishier.ts b/src/api/qishier.ts index a59088c..8ec8667 100644 --- a/src/api/qishier.ts +++ b/src/api/qishier.ts @@ -7,28 +7,33 @@ export interface EpisodeData { releasedAt: number timeLength: number videoUrl: string - raw?: Record } -export interface QishierCacheResponse { +export interface QishierListResponse { updatedAt: string | null name: string coverUrl: string displayType: number total: number - beginScoreMap: Record - pages: Record + years: number[] + page: number + pageSize: number + hasMore: boolean list: EpisodeData[] } -export function getAllQishierList() { - return axios.get('/api/qishier/all', { +export function getQishierList(page = 1, pageSize = 50) { + return axios.get('/api/qishier/list', { + params: { + page, + pageSize + }, timeout: 15000 }) } -export function getQishierStatus() { - return axios.get('/api/qishier/status', { +export function refreshQishierCache() { + return axios.post('/api/qishier/refresh', {}, { timeout: 15000 }) } diff --git a/src/types/plyr.d.ts b/src/types/plyr.d.ts new file mode 100644 index 0000000..9808be8 --- /dev/null +++ b/src/types/plyr.d.ts @@ -0,0 +1,21 @@ +declare module 'plyr' { + export interface PlyrOptions { + controls?: any[] + settings?: any[] + speed?: { + selected?: number + options?: number[] + } + [key: string]: any + } + + export default class Plyr { + constructor(target: string | HTMLElement, options?: PlyrOptions) + destroy(): void + play(): Promise + pause(): void + on(event: string, callback: (...args: any[]) => void): void + off(event: string, callback: (...args: any[]) => void): void + source: any + } +} diff --git a/src/views/QishierPlayer.vue b/src/views/QishierPlayer.vue index cc26319..a22c052 100644 --- a/src/views/QishierPlayer.vue +++ b/src/views/QishierPlayer.vue @@ -3,24 +3,21 @@

{{ columnInfo.name || '七十二家房客播放器' }}

-

共 {{ allEpisodeList.length }} 条,当前显示 {{ visibleEpisodeList.length }} 条

+

共 {{ total }} 条,当前显示 {{ episodeList.length }} 条

最后更新:{{ updatedAtText }}

- + +
- +

{{ currentEpisode.title }}

@@ -37,7 +34,7 @@
-
-
@@ -63,30 +60,41 @@ @@ -286,6 +354,7 @@ onBeforeUnmount(() => { .header-actions { display: flex; gap: 10px; + flex-wrap: wrap; } .action-btn { @@ -295,6 +364,7 @@ onBeforeUnmount(() => { background: #333; color: #fff; cursor: pointer; + text-decoration: none; } .action-btn:hover { @@ -409,6 +479,15 @@ onBeforeUnmount(() => { background: #444; } +.load-more-btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +:deep(.plyr) { + border-radius: 12px; +} + @media (max-width: 900px) { .layout { grid-template-columns: 1fr; diff --git a/vite.config.ts b/vite.config.ts index 23c5da9..bb1297c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,22 +2,55 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { fileURLToPath, URL } from 'node:url' -export default defineConfig({ +export default defineConfig(({ command }) => ({ plugins: [vue()], + resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, + server: { - port: 5173, + port: 23811, host: true, open: true, proxy: { '/api': { - target: 'http://localhost:3000', + target: 'http://127.0.0.1:23822/', + //target: 'http://8.134.120.132:23822/', + changeOrigin: true } - }, + } }, -}) + + // 打包配置 + build: { + // https://vite.dev/config/build-options.html + sourcemap: command === 'build' ? false : 'inline', + target: 'es2018', + + outDir: 'dist', + assetsDir: 'assets', + + chunkSizeWarningLimit: 2000, + + rollupOptions: { + output: { + // JS 拆分 + chunkFileNames: 'static/js/[name]-[hash].js', + entryFileNames: 'static/js/[name]-[hash].js', + + // 资源文件 + assetFileNames: 'static/[ext]/[name]-[hash].[ext]', + + // 手动拆包 + manualChunks: { + vue: ['vue'], + hls: ['hls.js'] + } + } + } + } +}))