列表开始就展示20条,
This commit is contained in:
parent
c5f2641377
commit
b77ff62c58
@ -1,4 +1,5 @@
|
||||
import { chromium } from 'playwright'
|
||||
import readlineSync from 'readline-sync'
|
||||
import { ensureDataFiles, readCache, writeCache, writeStatus } from './lib/cache.js'
|
||||
|
||||
const PAGE_URL = 'https://www1.gdtv.cn/tvColumn/768'
|
||||
@ -73,10 +74,7 @@ async function getPageScrollInfo(page) {
|
||||
return await page.evaluate(() => {
|
||||
return {
|
||||
scrollTop: window.scrollY || document.documentElement.scrollTop || document.body.scrollTop || 0,
|
||||
scrollHeight: Math.max(
|
||||
document.body.scrollHeight,
|
||||
document.documentElement.scrollHeight
|
||||
),
|
||||
scrollHeight: Math.max(document.body.scrollHeight, document.documentElement.scrollHeight),
|
||||
innerHeight: window.innerHeight
|
||||
}
|
||||
})
|
||||
@ -153,7 +151,12 @@ async function clickLoadMoreAndWait(page) {
|
||||
return !!response
|
||||
}
|
||||
|
||||
async function autoCollectByScrollAndClick(page, maxRounds = 120) {
|
||||
function askContinue() {
|
||||
const answer = readlineSync.question('\n页面已滑到底部,继续采集吗?(y/n): ')
|
||||
return answer.trim().toLowerCase() === 'y'
|
||||
}
|
||||
|
||||
async function autoCollectByScrollAndClick(page, maxRounds = 150) {
|
||||
let round = 0
|
||||
let noChangeRounds = 0
|
||||
let lastDomCount = await getDomItemCount(page)
|
||||
@ -214,15 +217,31 @@ async function autoCollectByScrollAndClick(page, maxRounds = 120) {
|
||||
await sleep(1800)
|
||||
|
||||
const retryButton = await findLoadMoreButton(page)
|
||||
if (!retryButton) {
|
||||
noChangeRounds += 1
|
||||
if (retryButton) {
|
||||
log('到底部后重新检测到“加载更多”按钮,继续点击')
|
||||
continue
|
||||
}
|
||||
|
||||
const shouldContinue = askContinue()
|
||||
if (!shouldContinue) {
|
||||
log('你选择结束采集')
|
||||
break
|
||||
}
|
||||
|
||||
log('你选择继续采集,尝试再次下滑检测')
|
||||
noChangeRounds = 0
|
||||
await sleep(1000)
|
||||
}
|
||||
|
||||
if (noChangeRounds >= 6) {
|
||||
log('连续多轮没有新内容,停止采集')
|
||||
const shouldContinue = askContinue()
|
||||
if (!shouldContinue) {
|
||||
log('连续多轮没有新内容,且你选择结束采集')
|
||||
break
|
||||
}
|
||||
log('你选择继续采集,重置无变化计数')
|
||||
noChangeRounds = 0
|
||||
}
|
||||
}
|
||||
|
||||
log(`自动采集流程结束,共执行 ${round} 轮`)
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
{
|
||||
"running": true,
|
||||
"lastMessage": "已采集第 2 页",
|
||||
"updatedAt": "2026-03-10T19:14:36.004Z",
|
||||
"capturedPages": 26,
|
||||
"totalItems": 1141
|
||||
"running": false,
|
||||
"lastMessage": "自动采集完成",
|
||||
"updatedAt": "2026-03-10T19:27:40.184Z"
|
||||
}
|
||||
@ -1,170 +1,170 @@
|
||||
{
|
||||
"updatedAt": "2026-03-10T19:14:35.996Z",
|
||||
"updatedAt": "2026-03-10T19:27:32.690Z",
|
||||
"name": "七十二家房客",
|
||||
"coverUrl": "https://p2-grtn.itouchtv.cn/image/20191010/0.15048946623517580e4543e24e68e4824OSS1570677193.jpg",
|
||||
"displayType": 0,
|
||||
"pages": {
|
||||
"1": {
|
||||
"currentPage": 1,
|
||||
"beginScore": 1669824000000,
|
||||
"beginScore": 1730390400000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:13:55.281Z"
|
||||
"capturedAt": "2026-03-10T19:27:28.717Z"
|
||||
},
|
||||
"2": {
|
||||
"currentPage": 2,
|
||||
"beginScore": 1669824000000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:14:35.996Z"
|
||||
"beginScore": 1730390400000,
|
||||
"count": 20,
|
||||
"capturedAt": "2026-03-10T19:27:31.309Z"
|
||||
},
|
||||
"3": {
|
||||
"currentPage": 3,
|
||||
"beginScore": 1701360000000,
|
||||
"beginScore": 1730390400000,
|
||||
"count": 0,
|
||||
"capturedAt": "2026-03-10T19:13:21.335Z"
|
||||
"capturedAt": "2026-03-10T19:27:32.690Z"
|
||||
},
|
||||
"4": {
|
||||
"currentPage": 4,
|
||||
"beginScore": 1763561820000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:26.676Z"
|
||||
"capturedAt": "2026-03-10T19:25:11.140Z"
|
||||
},
|
||||
"5": {
|
||||
"currentPage": 5,
|
||||
"beginScore": 1762962000000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:29.024Z"
|
||||
"capturedAt": "2026-03-10T19:25:13.417Z"
|
||||
},
|
||||
"6": {
|
||||
"currentPage": 6,
|
||||
"beginScore": 1762352220000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:33.591Z"
|
||||
"capturedAt": "2026-03-10T19:25:18.001Z"
|
||||
},
|
||||
"7": {
|
||||
"currentPage": 7,
|
||||
"beginScore": 1761746220000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:36.420Z"
|
||||
"capturedAt": "2026-03-10T19:25:20.845Z"
|
||||
},
|
||||
"8": {
|
||||
"currentPage": 8,
|
||||
"beginScore": 1761056280000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:39.251Z"
|
||||
"capturedAt": "2026-03-10T19:25:23.655Z"
|
||||
},
|
||||
"9": {
|
||||
"currentPage": 9,
|
||||
"beginScore": 1740315600000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:41.578Z"
|
||||
"capturedAt": "2026-03-10T19:25:26.285Z"
|
||||
},
|
||||
"10": {
|
||||
"currentPage": 10,
|
||||
"beginScore": 1738414800000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:44.739Z"
|
||||
"capturedAt": "2026-03-10T19:25:29.425Z"
|
||||
},
|
||||
"11": {
|
||||
"currentPage": 11,
|
||||
"beginScore": 1736514000000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:47.585Z"
|
||||
"capturedAt": "2026-03-10T19:25:32.213Z"
|
||||
},
|
||||
"12": {
|
||||
"currentPage": 12,
|
||||
"beginScore": 1734526800000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:50.449Z"
|
||||
"capturedAt": "2026-03-10T19:25:35.060Z"
|
||||
},
|
||||
"13": {
|
||||
"currentPage": 13,
|
||||
"beginScore": 1732714295000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:52.794Z"
|
||||
"capturedAt": "2026-03-10T19:25:37.402Z"
|
||||
},
|
||||
"14": {
|
||||
"currentPage": 14,
|
||||
"beginScore": 1730986358000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:11:57.376Z"
|
||||
"capturedAt": "2026-03-10T19:25:42.036Z"
|
||||
},
|
||||
"15": {
|
||||
"currentPage": 15,
|
||||
"beginScore": 1729171974000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:00.272Z"
|
||||
"capturedAt": "2026-03-10T19:25:44.872Z"
|
||||
},
|
||||
"16": {
|
||||
"currentPage": 16,
|
||||
"beginScore": 1727442000000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:03.129Z"
|
||||
"capturedAt": "2026-03-10T19:25:47.704Z"
|
||||
},
|
||||
"17": {
|
||||
"currentPage": 17,
|
||||
"beginScore": 1725713999999,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:05.452Z"
|
||||
"capturedAt": "2026-03-10T19:25:50.068Z"
|
||||
},
|
||||
"18": {
|
||||
"currentPage": 18,
|
||||
"beginScore": 1723899600000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:10.101Z"
|
||||
"capturedAt": "2026-03-10T19:25:54.781Z"
|
||||
},
|
||||
"19": {
|
||||
"currentPage": 19,
|
||||
"beginScore": 1721998800000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:12.989Z"
|
||||
"capturedAt": "2026-03-10T19:25:57.635Z"
|
||||
},
|
||||
"20": {
|
||||
"currentPage": 20,
|
||||
"beginScore": 1720186339000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:15.860Z"
|
||||
"capturedAt": "2026-03-10T19:26:00.455Z"
|
||||
},
|
||||
"21": {
|
||||
"currentPage": 21,
|
||||
"beginScore": 1718371929000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:18.172Z"
|
||||
"capturedAt": "2026-03-10T19:26:02.811Z"
|
||||
},
|
||||
"22": {
|
||||
"currentPage": 22,
|
||||
"beginScore": 1716641999999,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:21.399Z"
|
||||
"capturedAt": "2026-03-10T19:26:06.118Z"
|
||||
},
|
||||
"23": {
|
||||
"currentPage": 23,
|
||||
"beginScore": 1714827600000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:24.238Z"
|
||||
"capturedAt": "2026-03-10T19:26:08.954Z"
|
||||
},
|
||||
"24": {
|
||||
"currentPage": 24,
|
||||
"beginScore": 1713099599999,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:27.289Z"
|
||||
"capturedAt": "2026-03-10T19:26:11.917Z"
|
||||
},
|
||||
"25": {
|
||||
"currentPage": 25,
|
||||
"beginScore": 1711372007000,
|
||||
"count": 40,
|
||||
"capturedAt": "2026-03-10T19:12:29.500Z"
|
||||
"capturedAt": "2026-03-10T19:26:14.215Z"
|
||||
},
|
||||
"26": {
|
||||
"currentPage": 26,
|
||||
"beginScore": 1709559052000,
|
||||
"count": 0,
|
||||
"capturedAt": "2026-03-10T19:12:34.177Z"
|
||||
"capturedAt": "2026-03-10T19:26:19.076Z"
|
||||
}
|
||||
},
|
||||
"beginScoreMap": {
|
||||
"1": 1669824000000,
|
||||
"2": 1669824000000,
|
||||
"3": 1701360000000,
|
||||
"1": 1730390400000,
|
||||
"2": 1730390400000,
|
||||
"3": 1730390400000,
|
||||
"4": 1763561820000,
|
||||
"5": 1762962000000,
|
||||
"6": 1762352220000,
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="header">
|
||||
<div>
|
||||
<h1>{{ columnInfo.name || '七十二家房客播放器' }}</h1>
|
||||
<p>缓存 {{ episodeList.length }} 条</p>
|
||||
<p>共 {{ allEpisodeList.length }} 条,当前显示 {{ visibleEpisodeList.length }} 条</p>
|
||||
<p class="sub">最后更新:{{ updatedAtText }}</p>
|
||||
</div>
|
||||
|
||||
<div class="header-actions">
|
||||
<button class="action-btn" @click="fetchList">刷新数据</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layout">
|
||||
<div class="player-panel">
|
||||
<video
|
||||
@ -24,23 +30,31 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="errorMsg" class="error">{{ errorMsg }}</div>
|
||||
<div v-if="errorMsg" class="error">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-panel">
|
||||
<div
|
||||
v-for="item in episodeList"
|
||||
v-for="item in visibleEpisodeList"
|
||||
:key="item.id"
|
||||
class="episode-item"
|
||||
:class="{ active: currentEpisode?.id === item.id }"
|
||||
@click="playEpisode(item)"
|
||||
>
|
||||
<img :src="item.coverUrl" alt="" />
|
||||
<img :src="item.coverUrl" alt="" loading="lazy" />
|
||||
<div class="episode-text">
|
||||
<div class="title">{{ item.title }}</div>
|
||||
<div class="meta">{{ formatDate(item.releasedAt) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="load-more-wrap" v-if="visibleEpisodeList.length < allEpisodeList.length">
|
||||
<button class="load-more-btn" @click="loadMoreEpisodes">
|
||||
加载更多
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -59,33 +73,112 @@ const columnInfo = reactive({
|
||||
coverUrl: ''
|
||||
})
|
||||
|
||||
const episodeList = ref<EpisodeData[]>([])
|
||||
const allEpisodeList = ref<EpisodeData[]>([])
|
||||
const visibleEpisodeList = ref<EpisodeData[]>([])
|
||||
const currentEpisode = ref<EpisodeData | null>(null)
|
||||
const errorMsg = ref('')
|
||||
const updatedAt = ref<string | null>(null)
|
||||
|
||||
const pageSize = 20
|
||||
const currentVisibleCount = ref(pageSize)
|
||||
|
||||
const updatedAtText = computed(() => {
|
||||
if (!updatedAt.value) return '暂无缓存'
|
||||
return new Date(updatedAt.value).toLocaleString('zh-CN')
|
||||
})
|
||||
|
||||
function setCookie(name: string, value: string, days = 30) {
|
||||
const expires = new Date()
|
||||
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000)
|
||||
document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires.toUTCString()}; path=/`
|
||||
}
|
||||
|
||||
function getCookie(name: string): string | null {
|
||||
const nameEQ = `${name}=`
|
||||
const cookies = document.cookie.split(';')
|
||||
for (let cookie of cookies) {
|
||||
cookie = cookie.trim()
|
||||
if (cookie.indexOf(nameEQ) === 0) {
|
||||
return decodeURIComponent(cookie.substring(nameEQ.length))
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function updateVisibleList() {
|
||||
visibleEpisodeList.value = allEpisodeList.value.slice(0, currentVisibleCount.value)
|
||||
}
|
||||
|
||||
function loadMoreEpisodes() {
|
||||
currentVisibleCount.value += pageSize
|
||||
updateVisibleList()
|
||||
}
|
||||
|
||||
function destroyHls(): void {
|
||||
if (hlsRef.value) {
|
||||
hlsRef.value.destroy()
|
||||
hlsRef.value = null
|
||||
}
|
||||
|
||||
if (videoRef.value) {
|
||||
videoRef.value.ontimeupdate = null
|
||||
videoRef.value.onloadedmetadata = null
|
||||
}
|
||||
}
|
||||
|
||||
function bindProgressSave(video: HTMLVideoElement, item: EpisodeData) {
|
||||
video.ontimeupdate = () => {
|
||||
setCookie(`qishier_progress_${item.id}`, String(Math.floor(video.currentTime)), 30)
|
||||
}
|
||||
}
|
||||
|
||||
function restoreProgress(video: HTMLVideoElement, item: EpisodeData) {
|
||||
const savedTime = getCookie(`qishier_progress_${item.id}`)
|
||||
if (savedTime && !Number.isNaN(Number(savedTime))) {
|
||||
video.currentTime = Number(savedTime)
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchList(): Promise<void> {
|
||||
try {
|
||||
errorMsg.value = ''
|
||||
const res = await getAllQishierList()
|
||||
const data = res.data
|
||||
|
||||
console.log('前端拿到缓存:', {
|
||||
updatedAt: data.updatedAt,
|
||||
total: data.total,
|
||||
pages: Object.keys(data.pages || {}).length,
|
||||
listLength: Array.isArray(data.list) ? data.list.length : 0
|
||||
})
|
||||
|
||||
columnInfo.name = data.name || '七十二家房客'
|
||||
columnInfo.coverUrl = data.coverUrl || ''
|
||||
updatedAt.value = data.updatedAt || null
|
||||
episodeList.value = Array.isArray(data.list) ? data.list : []
|
||||
|
||||
if (episodeList.value.length > 0) {
|
||||
await playEpisode(episodeList.value[0])
|
||||
allEpisodeList.value = Array.isArray(data.list) ? data.list : []
|
||||
currentVisibleCount.value = pageSize
|
||||
updateVisibleList()
|
||||
|
||||
if (allEpisodeList.value.length > 0) {
|
||||
const savedEpisodeId = getCookie('qishier_current_episode_id')
|
||||
const savedEpisode = allEpisodeList.value.find((item) => item.id === savedEpisodeId)
|
||||
|
||||
if (savedEpisode) {
|
||||
const index = allEpisodeList.value.findIndex((item) => item.id === savedEpisode.id)
|
||||
if (index >= 0 && index + 1 > currentVisibleCount.value) {
|
||||
currentVisibleCount.value = Math.ceil((index + 1) / pageSize) * pageSize
|
||||
updateVisibleList()
|
||||
}
|
||||
await playEpisode(savedEpisode)
|
||||
} else {
|
||||
await playEpisode(allEpisodeList.value[0])
|
||||
}
|
||||
} else {
|
||||
errorMsg.value = '缓存里还没有节目数据,请先运行采集脚本'
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('fetchList error:', error)
|
||||
errorMsg.value = error?.message || '读取缓存失败'
|
||||
}
|
||||
}
|
||||
@ -93,6 +186,7 @@ async function fetchList(): Promise<void> {
|
||||
async function playEpisode(item: EpisodeData): Promise<void> {
|
||||
currentEpisode.value = item
|
||||
errorMsg.value = ''
|
||||
setCookie('qishier_current_episode_id', item.id, 30)
|
||||
|
||||
await nextTick()
|
||||
|
||||
@ -104,6 +198,12 @@ async function playEpisode(item: EpisodeData): Promise<void> {
|
||||
if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
video.src = item.videoUrl
|
||||
video.load()
|
||||
|
||||
video.onloadedmetadata = () => {
|
||||
restoreProgress(video, item)
|
||||
}
|
||||
|
||||
bindProgressSave(video, item)
|
||||
void video.play().catch(() => {})
|
||||
return
|
||||
}
|
||||
@ -114,6 +214,8 @@ async function playEpisode(item: EpisodeData): Promise<void> {
|
||||
hls.attachMedia(video)
|
||||
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
restoreProgress(video, item)
|
||||
bindProgressSave(video, item)
|
||||
void video.play().catch(() => {})
|
||||
})
|
||||
|
||||
@ -127,13 +229,6 @@ async function playEpisode(item: EpisodeData): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function destroyHls(): void {
|
||||
if (hlsRef.value) {
|
||||
hlsRef.value.destroy()
|
||||
hlsRef.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function formatDate(timestamp: number): string {
|
||||
if (!timestamp) return '--'
|
||||
const d = new Date(timestamp)
|
||||
@ -166,45 +261,93 @@ onBeforeUnmount(() => {
|
||||
background: #111;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
.sub {
|
||||
|
||||
.header p {
|
||||
margin: 0 0 6px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.sub {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 10px 16px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1.6fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.player-panel,
|
||||
.list-panel {
|
||||
background: #1b1b1b;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.video {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
background: #000;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.info h2 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 20px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.info p {
|
||||
margin: 0;
|
||||
color: #aaa;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.error {
|
||||
padding: 0 16px 16px;
|
||||
color: #ff7875;
|
||||
}
|
||||
|
||||
.list-panel {
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.episode-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
@ -212,13 +355,17 @@ onBeforeUnmount(() => {
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 10px;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.episode-item:hover {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.episode-item.active {
|
||||
background: rgba(255, 193, 7, 0.16);
|
||||
}
|
||||
|
||||
.episode-item img {
|
||||
width: 120px;
|
||||
height: 68px;
|
||||
@ -226,23 +373,49 @@ onBeforeUnmount(() => {
|
||||
border-radius: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.episode-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.meta {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.load-more-wrap {
|
||||
padding: 12px 0 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.load-more-btn {
|
||||
padding: 10px 18px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.load-more-btn:hover {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -10,11 +10,14 @@ export default defineConfig({
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
host: true,
|
||||
open: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:3000',
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user