优化黑夜
This commit is contained in:
parent
83e23b7365
commit
e4a0ac71cc
149
vue/src/App.vue
149
vue/src/App.vue
@ -44,14 +44,39 @@
|
|||||||
<input
|
<input
|
||||||
v-model="searchKeyword"
|
v-model="searchKeyword"
|
||||||
type="search"
|
type="search"
|
||||||
placeholder="请输入标题、标签、资源库或维护人"
|
placeholder="🔍 输入关键词搜索..."
|
||||||
@focus="isSearchFocused = true"
|
@focus="isSearchFocused = true"
|
||||||
@blur="isSearchFocused = false"
|
@blur="isSearchFocused = false"
|
||||||
@keydown.enter="handleHeroSearch"
|
@keydown.enter="handleHeroSearch"
|
||||||
/>
|
/>
|
||||||
<button type="button" @click="handleHeroSearch">搜索</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 黑夜/白天模式切换按钮 -->
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="theme-toggle-btn"
|
||||||
|
:class="{ 'theme-toggle-btn--dark': isDarkMode }"
|
||||||
|
@click="toggleDarkMode"
|
||||||
|
:title="isDarkMode ? '切换到白天模式' : '切换到黑夜模式'"
|
||||||
|
>
|
||||||
|
<svg class="theme-toggle-btn__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<g v-if="!isDarkMode" class="theme-icon--sun">
|
||||||
|
<circle cx="12" cy="12" r="5"></circle>
|
||||||
|
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||||||
|
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||||||
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||||||
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||||||
|
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||||||
|
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||||||
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||||||
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||||||
|
</g>
|
||||||
|
<g v-if="isDarkMode" class="theme-icon--moon">
|
||||||
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- 快捷入口按钮和悬浮面板 -->
|
<!-- 快捷入口按钮和悬浮面板 -->
|
||||||
<div class="quick-access-wrapper" ref="quickAccessWrapperRef" @mouseenter="handleQuickAccessEnter" @mouseleave="handleQuickAccessWrapperLeave">
|
<div class="quick-access-wrapper" ref="quickAccessWrapperRef" @mouseenter="handleQuickAccessEnter" @mouseleave="handleQuickAccessWrapperLeave">
|
||||||
<button
|
<button
|
||||||
@ -366,6 +391,14 @@
|
|||||||
<button type="button" class="folder-tree-actions__btn" @click="handleOpenScraperLogDialog">
|
<button type="button" class="folder-tree-actions__btn" @click="handleOpenScraperLogDialog">
|
||||||
📋 刮削日志
|
📋 刮削日志
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="folder-tree-actions__btn folder-tree-actions__btn--refresh"
|
||||||
|
@click="handleRefreshFolders"
|
||||||
|
title="刷新当前网盘文件夹列表"
|
||||||
|
>
|
||||||
|
🗂️ 刷新文件夹
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="folder-tree-actions__btn"
|
class="folder-tree-actions__btn"
|
||||||
@ -458,9 +491,13 @@
|
|||||||
{{ getStatusText(scrapeStatusData.status) }}
|
{{ getStatusText(scrapeStatusData.status) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="scrape-status-message">
|
<div v-if="scrapeStatusData.message" class="scrape-status-message">
|
||||||
{{ scrapeStatusData.message }}
|
{{ scrapeStatusData.message }}
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 失败时显示详细错误信息 -->
|
||||||
|
<div v-if="scrapeStatusData.status === 'failed' && scrapeStatusData.error" class="scrape-status-error">
|
||||||
|
<strong>失败原因:</strong>{{ scrapeStatusData.error }}
|
||||||
|
</div>
|
||||||
<div v-if="scrapeStatusData.posterUrl" class="scrape-status-poster">
|
<div v-if="scrapeStatusData.posterUrl" class="scrape-status-poster">
|
||||||
<img :src="scrapeStatusData.posterUrl" alt="海报" />
|
<img :src="scrapeStatusData.posterUrl" alt="海报" />
|
||||||
</div>
|
</div>
|
||||||
@ -564,13 +601,13 @@
|
|||||||
<div class="log-entry__header" @click="toggleLogDetail(log.id)">
|
<div class="log-entry__header" @click="toggleLogDetail(log.id)">
|
||||||
<div class="log-entry__summary">
|
<div class="log-entry__summary">
|
||||||
<span class="log-entry__toggle">{{ log.showDetail ? '▾' : '▸' }}</span>
|
<span class="log-entry__toggle">{{ log.showDetail ? '▾' : '▸' }}</span>
|
||||||
<span class="log-entry__time">{{ log.scanTime || log.createTime || '-' }}</span>
|
<span class="log-entry__time">{{ log.scanTime || '-' }}</span>
|
||||||
<span class="log-entry__desc">{{ log.description || '刮削任务执行记录' }}</span>
|
<span class="log-entry__desc">{{ getStatusText(log.status) || '刮削任务执行记录' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="log-entry__stats">
|
<div class="log-entry__stats">
|
||||||
<span class="stat-total">总共: {{ log.totalMovies || log.total || 0 }}</span>
|
<span class="stat-total">总共: {{ log.totalMovies || 0 }}</span>
|
||||||
<span class="stat-success">成功: {{ log.successCount || log.success || 0 }}</span>
|
<span class="stat-success">成功: {{ log.successCount || 0 }}</span>
|
||||||
<span class="stat-fail">失败: {{ log.failCount || log.failed || 0 }}</span>
|
<span class="stat-fail">失败: {{ log.failCount || 0 }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -578,11 +615,10 @@
|
|||||||
<Transition name="slide-down">
|
<Transition name="slide-down">
|
||||||
<div v-if="log.showDetail && log.detailList && log.detailList.length > 0" class="log-entry__detail">
|
<div v-if="log.showDetail && log.detailList && log.detailList.length > 0" class="log-entry__detail">
|
||||||
<div v-for="(item, idx) in log.detailList" :key="idx" class="detail-item">
|
<div v-for="(item, idx) in log.detailList" :key="idx" class="detail-item">
|
||||||
<span class="detail-item__name">{{ item.movieName || item.title || '未知电影' }}</span>
|
<span class="detail-item__name">👤 {{ item.username || '用户 ' + (item.userId || idx) }}</span>
|
||||||
<span class="detail-item__status" :class="`detail-item__status--${item.status}`">
|
<span class="detail-item__status detail-item__status--info">
|
||||||
{{ item.status === 'success' ? '✓ 成功' : item.status === 'failed' ? '✗ 失败' : '⋯ 处理中' }}
|
处理: {{ item.processed || 0 }} | 成功: {{ item.success || 0 }} | 失败: {{ item.failed || 0 }} | 跳过: {{ item.skipped || 0 }}
|
||||||
</span>
|
</span>
|
||||||
<span v-if="item.message" class="detail-item__message">{{ item.message }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
@ -845,6 +881,7 @@ const searchKeyword = ref('')
|
|||||||
const isSearchFocused = ref(false)
|
const isSearchFocused = ref(false)
|
||||||
const hasSearched = ref(false)
|
const hasSearched = ref(false)
|
||||||
const currentView = ref('poster')
|
const currentView = ref('poster')
|
||||||
|
const isDarkMode = ref(false) // 黑夜模式开关
|
||||||
const activeSheetId = ref('')
|
const activeSheetId = ref('')
|
||||||
const showRecordDialog = ref(false)
|
const showRecordDialog = ref(false)
|
||||||
const showFolderDialog = ref(false)
|
const showFolderDialog = ref(false)
|
||||||
@ -1678,11 +1715,52 @@ async function loadMoreScraperLogs() {
|
|||||||
|
|
||||||
const logsRes = await fetchScraperLogs(params)
|
const logsRes = await fetchScraperLogs(params)
|
||||||
|
|
||||||
|
console.log('[刮削日志] 后端返回原始数据:', logsRes)
|
||||||
|
|
||||||
if (logsRes?.code === 200 && Array.isArray(logsRes?.data?.list)) {
|
if (logsRes?.code === 200 && Array.isArray(logsRes?.data?.list)) {
|
||||||
const newLogs = logsRes.data.list
|
const newLogs = logsRes.data.list
|
||||||
|
|
||||||
|
console.log('[刮削日志] 日志列表数据:', newLogs)
|
||||||
|
console.log('[刮削日志] 第一条日志的字段:', Object.keys(newLogs[0] || {}))
|
||||||
|
if (newLogs[0]) {
|
||||||
|
console.log('[刮削日志] 第一条日志完整数据:', JSON.stringify(newLogs[0], null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标准化日志数据字段(兼容不同的字段名)
|
||||||
|
const normalizedLogs = newLogs.map(log => {
|
||||||
|
// 解析 detail_json 字符串为数组
|
||||||
|
let detailList = []
|
||||||
|
if (log.detail_json) {
|
||||||
|
try {
|
||||||
|
detailList = typeof log.detail_json === 'string'
|
||||||
|
? JSON.parse(log.detail_json)
|
||||||
|
: log.detail_json
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('解析 detail_json 失败:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...log,
|
||||||
|
// 确保 showDetail 属性可设置
|
||||||
|
showDetail: false,
|
||||||
|
// 后端字段映射(使用实际的后端字段名)
|
||||||
|
totalMovies: log.total_processed ?? log.totalMovies ?? log.total ?? 0,
|
||||||
|
successCount: log.total_success ?? log.successCount ?? log.success ?? 0,
|
||||||
|
failCount: log.total_failed ?? log.failCount ?? log.failed ?? 0,
|
||||||
|
totalSkipped: log.total_skipped ?? 0,
|
||||||
|
scanTime: log.start_time ?? log.create_time ?? log.createTime ?? '-',
|
||||||
|
endTime: log.end_time ?? '',
|
||||||
|
durationMs: log.duration_ms ?? 0,
|
||||||
|
status: log.status ?? 'unknown',
|
||||||
|
triggerType: log.trigger_type ?? 'manual',
|
||||||
|
// 解析后的详细信息列表
|
||||||
|
detailList: detailList,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 追加到现有日志列表
|
// 追加到现有日志列表
|
||||||
scraperLogs.value.push(...newLogs)
|
scraperLogs.value.push(...normalizedLogs)
|
||||||
|
|
||||||
// 更新分页信息
|
// 更新分页信息
|
||||||
scraperLogPagination.total = logsRes.data.total || 0
|
scraperLogPagination.total = logsRes.data.total || 0
|
||||||
@ -2491,6 +2569,28 @@ function preloadImage(url) {
|
|||||||
img.src = url
|
img.src = url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新当前网盘文件夹列表
|
||||||
|
*/
|
||||||
|
async function handleRefreshFolders() {
|
||||||
|
const driveId = activeSheetId.value
|
||||||
|
if (!driveId) {
|
||||||
|
setErrorMessage('请先选择一个网盘')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
driveLoading.value = true
|
||||||
|
try {
|
||||||
|
await loadDriveFolders(driveId)
|
||||||
|
setSuccessMessage('文件夹列表已刷新')
|
||||||
|
} catch (err) {
|
||||||
|
console.error('刷新文件夹列表失败', err)
|
||||||
|
setErrorMessage(err?.message || '刷新文件夹列表失败')
|
||||||
|
} finally {
|
||||||
|
driveLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 刮削状态查询
|
// 刮削状态查询
|
||||||
function handleScrapeStatus(node) {
|
function handleScrapeStatus(node) {
|
||||||
// 只有文件类型才显示刮削状态
|
// 只有文件类型才显示刮削状态
|
||||||
@ -2521,7 +2621,7 @@ function handleScrapeStatus(node) {
|
|||||||
rating: data.rating,
|
rating: data.rating,
|
||||||
category: data.category,
|
category: data.category,
|
||||||
tags: data.tags,
|
tags: data.tags,
|
||||||
error: data.error,
|
error: data.error || data.message,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setErrorMessage(result?.message || '查询刮削状态失败')
|
setErrorMessage(result?.message || '查询刮削状态失败')
|
||||||
@ -2805,6 +2905,27 @@ onMounted(() => {
|
|||||||
resumeFeaturedAutoplay()
|
resumeFeaturedAutoplay()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 黑夜/白天模式切换函数
|
||||||
|
function toggleDarkMode() {
|
||||||
|
isDarkMode.value = !isDarkMode.value
|
||||||
|
// 更新 body 的 class
|
||||||
|
document.body.classList.toggle('dark-mode', isDarkMode.value)
|
||||||
|
// 保存到 localStorage
|
||||||
|
localStorage.setItem('darkMode', isDarkMode.value ? '1' : '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 localStorage 读取黑夜模式偏好
|
||||||
|
function initDarkMode() {
|
||||||
|
const savedMode = localStorage.getItem('darkMode')
|
||||||
|
if (savedMode === '1') {
|
||||||
|
isDarkMode.value = true
|
||||||
|
document.body.classList.add('dark-mode')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在 onMounted 之前调用初始化
|
||||||
|
initDarkMode()
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('wangpan:unauthorized', handleUnauthorized)
|
window.removeEventListener('wangpan:unauthorized', handleUnauthorized)
|
||||||
window.removeEventListener('wangpan:forbidden', handleForbidden)
|
window.removeEventListener('wangpan:forbidden', handleForbidden)
|
||||||
|
|||||||
1246
vue/src/style.css
1246
vue/src/style.css
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user