feat: 新增多语言图标生成脚本与视图切换功能,优化分页与暗黑模式
1. 新增Python/JS版图标生成脚本,批量创建标准化SVG图标 2. 重构页面视图切换逻辑,添加平滑过渡动画 3. 升级登录日志分页系统,支持页码跳转与页大小调整 4. 完善暗黑模式样式适配,覆盖更多UI组件 5. 优化海报展示适配,根据图片比例自动调整布局
This commit is contained in:
parent
a889b83959
commit
42146aedf4
BIN
create_icons.py
Normal file
BIN
create_icons.py
Normal file
Binary file not shown.
20
gen_icons.js
Normal file
20
gen_icons.js
Normal file
@ -0,0 +1,20 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const iconsDir = path.join(__dirname, 'public', 'icons');
|
||||
if (!fs.existsSync(iconsDir)) fs.mkdirSync(iconsDir, { recursive: true });
|
||||
const icons = {
|
||||
home: { path: 'M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z M9 22v-10l6 6v-10', label: 'Home' },
|
||||
search: { d: 'circle cx=11 cy=11 r=8 line x1=21 y1=21 x2=16.65 y2=16.65', label: 'Search' },
|
||||
user: { d: 'path d=M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2 circle cx=12 cy=7 r=4', label: 'User' },
|
||||
folder: { d: 'path d=M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z', fill: true, label: 'Folder' },
|
||||
file: { d: 'path d=M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z polyline points=14 2 14 8 20 8', label: 'File' },
|
||||
settings: { d: 'circle cx=12 cy=12 r=3 path d=M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0', label: 'Settings' },
|
||||
close: { d: 'line x1=18 y1=6 x2=6 y2=18 line x1=6 y1=6 x2=18 y2=18', label: 'Close' },
|
||||
check: { d: 'polyline points=20 6 9 17 4 12', color: '#22c55e', label: 'Check' },
|
||||
delete: { d: 'polyline points=3 6 5 6 21 6 path d=M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2', color: '#ef4444', label: 'Delete' },
|
||||
add: { d: 'circle cx=12 cy=12 r=10 line x1=12 y1=8 x2=12 y2=16 line x1=8 y1=12 x2=16 y2=12', label: 'Add' },
|
||||
download: { d: 'path d=M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4 polyline points=7 10 12 15 17 10 line x1=12 y1=15 x2=12 y2=3', label: 'Download' },
|
||||
upload: { d: 'path d=M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4 polyline points=17 8 12 3 7 8 line x1=12 y1=3 x2=12 y2=15', label: 'Upload' },
|
||||
share: { d: 'circle cx=18 cy=5 r=3 circle cx=6 cy=12 r=3 circle cx=18 cy=19 r=3 line x1=8.59 y1=13.51 x2=15.42 y2=17.49 line x1=15.41 y1=6.51 x2=8.59 y2=10.49', label: 'Share' },
|
||||
favorite: { d: 'path d=M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z', fill: true, label: 'Favorite' },
|
||||
cloud: { d: 'path d=M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z', label: 'Cloud' },
|
||||
175
gen_icons.py
Normal file
175
gen_icons.py
Normal file
@ -0,0 +1,175 @@
|
||||
# Icon Generator Script
|
||||
import os
|
||||
|
||||
icons_dir = 'public/icons'
|
||||
os.makedirs(icons_dir, exist_ok=True)
|
||||
|
||||
# Color scheme
|
||||
PRIMARY_COLOR = '#6366f1'
|
||||
SUCCESS_COLOR = '#22c55e'
|
||||
DANGER_COLOR = '#ef4444'
|
||||
|
||||
def create_svg(filename, elements, color=PRIMARY_COLOR, fill=False):
|
||||
"""Create an SVG icon file"""
|
||||
fill_attr = 'fill "{}"'.format(color) if fill else 'fill="none"'
|
||||
svg_content = '''<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" {} stroke="{}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
{}
|
||||
</svg>'''.format(fill_attr, color, '\n'.join(elements))
|
||||
|
||||
filepath = os.path.join(icons_dir, filename)
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
f.write(svg_content)
|
||||
print(f'Created: {filename}')
|
||||
|
||||
# Home icon
|
||||
create_svg('home.svg', [
|
||||
'<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>',
|
||||
'<polyline points="9 22 9 12 15 12 15 22"/>'
|
||||
])
|
||||
|
||||
# Search icon
|
||||
create_svg('search.svg', [
|
||||
'<circle cx="11" cy="11" r="8"/>',
|
||||
'<line x1="21" y1="21" x2="16.65" y2="16.65"/>'
|
||||
])
|
||||
|
||||
# Settings icon
|
||||
create_svg('settings.svg', [
|
||||
'<circle cx="12" cy="12" r="3"/>',
|
||||
'<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>'
|
||||
])
|
||||
|
||||
# User icon
|
||||
create_svg('user.svg', [
|
||||
'<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>',
|
||||
'<circle cx="12" cy="7" r="4"/>'
|
||||
])
|
||||
|
||||
# Folder icon (filled)
|
||||
create_svg('folder.svg', [
|
||||
'<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>'
|
||||
], fill=True)
|
||||
|
||||
# File icon
|
||||
create_svg('file.svg', [
|
||||
'<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>',
|
||||
'<polyline points="14 2 14 8 20 8"/>'
|
||||
])
|
||||
|
||||
# Download icon
|
||||
create_svg('download.svg', [
|
||||
'<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>',
|
||||
'<polyline points="7 10 12 15 17 10"/>',
|
||||
'<line x1="12" y1="15" x2="12" y2="3"/>'
|
||||
])
|
||||
|
||||
# Upload icon
|
||||
create_svg('upload.svg', [
|
||||
'<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>',
|
||||
'<polyline points="17 8 12 3 7 8"/>',
|
||||
'<line x1="12" y1="3" x2="12" y2="15"/>'
|
||||
])
|
||||
|
||||
# Share icon
|
||||
create_svg('share.svg', [
|
||||
'<circle cx="18" cy="5" r="3"/>',
|
||||
'<circle cx="6" cy="12" r="3"/>',
|
||||
'<circle cx="18" cy="19" r="3"/>',
|
||||
'<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/>',
|
||||
'<line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>'
|
||||
])
|
||||
|
||||
# Favorite/Like icon (filled)
|
||||
create_svg('favorite.svg', [
|
||||
'<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>'
|
||||
], fill=True)
|
||||
|
||||
# Delete icon
|
||||
create_svg('delete.svg', [
|
||||
'<polyline points="3 6 5 6 21 6"/>',
|
||||
'<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>'
|
||||
], color=DANGER_COLOR)
|
||||
|
||||
# Edit icon
|
||||
create_svg('edit.svg', [
|
||||
'<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>',
|
||||
'<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>'
|
||||
])
|
||||
|
||||
# Add icon
|
||||
create_svg('add.svg', [
|
||||
'<circle cx="12" cy="12" r="10"/>',
|
||||
'<line x1="12" y1="8" x2="12" y2="16"/>',
|
||||
'<line x1="8" y1="12" x2="16" y2="12"/>'
|
||||
])
|
||||
|
||||
# Back/Arrow Left icon
|
||||
create_svg('back.svg', [
|
||||
'<line x1="19" y1="12" x2="5" y2="12"/>',
|
||||
'<polyline points="12 19 5 12 12 5"/>'
|
||||
])
|
||||
|
||||
# More/Options icon (three dots)
|
||||
create_svg('more.svg', [
|
||||
'<circle cx="12" cy="12" r="1"/>',
|
||||
'<circle cx="19" cy="12" r="1"/>',
|
||||
'<circle cx="5" cy="12" r="1"/>'
|
||||
], fill=True)
|
||||
|
||||
# Cloud icon
|
||||
create_svg('cloud.svg', [
|
||||
'<path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/>'
|
||||
])
|
||||
|
||||
# Drive/Server icon
|
||||
create_svg('drive.svg', [
|
||||
'<rect x="2" y="6" width="20" height="12" rx="2"/>',
|
||||
'<circle cx="12" cy="12" r="2"/>',
|
||||
'<path d="M6 12h.01 M18 12h.01"/>'
|
||||
])
|
||||
|
||||
# Log/File Text icon
|
||||
create_svg('log.svg', [
|
||||
'<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>',
|
||||
'<polyline points="14 2 14 8 20 8"/>',
|
||||
'<line x1="16" y1="13" x2="8" y2="13"/>',
|
||||
'<line x1="16" y1="17" x2="8" y2="17"/>',
|
||||
'<polyline points="10 9 9 9 8 9"/>'
|
||||
])
|
||||
|
||||
# Close icon
|
||||
create_svg('close.svg', [
|
||||
'<line x1="18" y1="6" x2="6" y2="18"/>',
|
||||
'<line x1="6" y1="6" x2="18" y2="18"/>'
|
||||
])
|
||||
|
||||
# Check/Verify icon
|
||||
create_svg('check.svg', [
|
||||
'<polyline points="20 6 9 17 4 12"/>'
|
||||
], color=SUCCESS_COLOR)
|
||||
|
||||
# Play icon
|
||||
create_svg('play.svg', [
|
||||
'<polygon points="5 3 19 12 5 21 5 3"/>'
|
||||
], fill=True)
|
||||
|
||||
# Pause icon
|
||||
create_svg('pause.svg', [
|
||||
'<rect x="6" y="4" width="4" height="16"/>',
|
||||
'<rect x="14" y="4" width="4" height="16"/>'
|
||||
])
|
||||
|
||||
# Eye icon
|
||||
create_svg('eye.svg', [
|
||||
'<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>',
|
||||
'<circle cx="12" cy="12" r="3"/>'
|
||||
])
|
||||
|
||||
# Eye Off icon
|
||||
create_svg('eye-off.svg', [
|
||||
'<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/>',
|
||||
'<line x1="1" y1="1" x2="23" y2="23"/>'
|
||||
])
|
||||
|
||||
print(f'\nTotal icons created: {len(os.listdir(icons_dir)) - 1}') # Exclude test.txt
|
||||
print('Icon generation complete!')
|
||||
255
src/App.vue
255
src/App.vue
@ -179,13 +179,10 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="user-menu__divider"></div>
|
||||
<button type="button" class="user-menu__item" @click="switchToPoster">
|
||||
<span class="user-menu__icon">🎬</span>
|
||||
<span>海报展示</span>
|
||||
</button>
|
||||
<button type="button" class="user-menu__item" @click="handleSwitchToManage">
|
||||
<span class="user-menu__icon">📋</span>
|
||||
<span>控制台</span>
|
||||
<button type="button" class="user-menu__item user-menu__item--toggle" @click="toggleView" :title="currentView === 'manage' ? '切换到海报展示' : '切换到控制台'">
|
||||
<span class="user-menu__icon">{{ currentView === 'manage' ? '🎬' : '📋' }}</span>
|
||||
<span>{{ currentView === 'manage' ? '海报展示' : '控制台' }}</span>
|
||||
<span class="user-menu__toggle-icon">⇄</span>
|
||||
</button>
|
||||
<button type="button" class="user-menu__item" @click="handleTmdbConfig">
|
||||
<span class="user-menu__icon">⚙️</span>
|
||||
@ -213,10 +210,12 @@
|
||||
</section>
|
||||
|
||||
<header class="topbar dashboard-topbar">
|
||||
<div class="dashboard-brand">
|
||||
<strong>{{ currentView === 'manage' ? '控制台' : '海报展示' }}</strong>
|
||||
<span>{{ currentView === 'manage' ? '管理网盘资源与账号数据' : '根据网盘内容进行 TMDB 海报刮削展示' }}</span>
|
||||
</div>
|
||||
<Transition name="title-slide" mode="out-in">
|
||||
<div class="dashboard-brand" :key="currentView">
|
||||
<strong>{{ currentView === 'manage' ? '控制台' : '海报展示' }}</strong>
|
||||
<span>{{ currentView === 'manage' ? '管理网盘资源与账号数据' : '根据网盘内容进行 TMDB 海报刮削展示' }}</span>
|
||||
</div>
|
||||
</Transition>
|
||||
</header>
|
||||
|
||||
<section v-if="currentView === 'poster' && !hasSearched && !isSearchFocused" class="hero-carousel" aria-label="每日推荐海报">
|
||||
@ -246,7 +245,8 @@
|
||||
:src="activeFeaturedPoster.posterUrl"
|
||||
:alt="activeFeaturedPoster.title"
|
||||
class="hero-bg-image"
|
||||
@load="heroImageLoaded = true"
|
||||
:class="{ 'hero-bg-image--wide': isHeroWideDynamic }"
|
||||
@load="handleHeroImageLoad"
|
||||
/>
|
||||
|
||||
<div class="hero-main__overlay">
|
||||
@ -299,9 +299,11 @@
|
||||
</Transition>
|
||||
|
||||
|
||||
<template v-if="!isLoading && currentView === 'manage' && !isSearchFocused">
|
||||
<!-- 快捷图标区域 -->
|
||||
<section ref="quickIconsRef" v-if="iconList.length || !hasSearched" class="quick-icons">
|
||||
<Transition name="view-slide" mode="out-in">
|
||||
<div :key="currentView">
|
||||
<template v-if="!isLoading && currentView === 'manage' && !isSearchFocused">
|
||||
<!-- 快捷图标区域 -->
|
||||
<section ref="quickIconsRef" v-if="iconList.length || !hasSearched" class="quick-icons">
|
||||
<div class="quick-icons__header">
|
||||
<h3>快捷入口</h3>
|
||||
<div class="quick-icons__actions">
|
||||
@ -557,27 +559,29 @@
|
||||
正在查询刮削状态...
|
||||
</div>
|
||||
</SimpleDialog>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else-if="!isLoading && currentView === 'poster' && !hasSearched && !isSearchFocused">
|
||||
<section class="poster-showcase">
|
||||
<div class="content-toolbar">
|
||||
<div>
|
||||
<h2>海报展示</h2>
|
||||
<p>根据网盘管理中的标题与海报地址进行 TMDB 匹配展示,可作为刮削结果预览。</p>
|
||||
<template v-else-if="!isLoading && currentView === 'poster' && !hasSearched && !isSearchFocused">
|
||||
<section class="poster-showcase">
|
||||
<div class="content-toolbar">
|
||||
<div>
|
||||
<h2>海报展示</h2>
|
||||
<p>根据网盘管理中的标题与海报地址进行 TMDB 匹配展示,可作为刮削结果预览。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="loadingPosterShowcase" class="poster-list-loading">
|
||||
正在加载海报数据...
|
||||
</div>
|
||||
<div v-else class="poster-list poster-list--showcase">
|
||||
<div v-for="item in posterShowcaseData" :key="item.id" class="poster-card-wrap">
|
||||
<PosterCard :item="item" :readonly="true" />
|
||||
<div v-if="loadingPosterShowcase" class="poster-list-loading">
|
||||
正在加载海报数据...
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<div v-else class="poster-list poster-list--showcase">
|
||||
<div v-for="item in posterShowcaseData" :key="item.id" class="poster-card-wrap">
|
||||
<PosterCard :item="item" :readonly="true" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 海报展示的搜索结果 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<template v-if="hasSearched && currentView === 'poster'">
|
||||
@ -763,21 +767,60 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页信息和加载更多按钮 -->
|
||||
<!-- 分页控制栏 -->
|
||||
<div v-if="logPagination.total > 0" class="log-pagination">
|
||||
<div class="pagination-info">
|
||||
已加载 {{ loginLogs.length }} / {{ logPagination.total }} 条
|
||||
共 {{ logPagination.total }} 条
|
||||
</div>
|
||||
<div class="pagination-pagesize">
|
||||
<select v-model.number="logPagination.pageSize" @change="handleLogPageChange">
|
||||
<option :value="10">10条/页</option>
|
||||
<option :value="20">20条/页</option>
|
||||
<option :value="50">50条/页</option>
|
||||
<option :value="100">100条/页</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="pagination-nav">
|
||||
<button
|
||||
type="button"
|
||||
class="pagination-btn pagination-btn--prev"
|
||||
:disabled="logPagination.pageNo <= 1"
|
||||
@click="handleLogPageChangeTo(logPagination.pageNo - 1)"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<span class="pagination-current-page">
|
||||
{{ logPagination.pageNo }} / {{ logPagination.totalPages }}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="pagination-btn pagination-btn--next"
|
||||
:disabled="logPagination.pageNo >= logPagination.totalPages"
|
||||
@click="handleLogPageChangeTo(logPagination.pageNo + 1)"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
<div class="pagination-go">
|
||||
前往
|
||||
<input
|
||||
v-model.number="logGoToPage"
|
||||
type="number"
|
||||
class="go-page-input"
|
||||
min="1"
|
||||
:max="logPagination.totalPages"
|
||||
@keyup.enter="handleGoToPage"
|
||||
/>
|
||||
页
|
||||
<button
|
||||
type="button"
|
||||
class="pagination-btn pagination-btn--go"
|
||||
@click="handleGoToPage"
|
||||
:disabled="logGoToPage < 1 || logGoToPage > logPagination.totalPages"
|
||||
>
|
||||
确定
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
v-if="logPagination.hasMore"
|
||||
type="button"
|
||||
class="load-more-btn"
|
||||
@click="handleLoadMoreLogs"
|
||||
:disabled="loadingLogs"
|
||||
>
|
||||
{{ loadingLogs ? '加载中...' : '加载更多' }}
|
||||
</button>
|
||||
<div v-else class="no-more">—— 已加载全部 ——</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@ -974,6 +1017,7 @@ const confirmConfig = reactive({
|
||||
})
|
||||
const showLogDialog = ref(false)
|
||||
const loadingLogs = ref(false)
|
||||
const logGoToPage = ref(1) // 前往指定页输入框
|
||||
const logFilter = reactive({
|
||||
username: '', // 用户名筛选
|
||||
result: '', // 结果筛选:success/fail(留空表示全部)
|
||||
@ -1070,6 +1114,8 @@ const loadingRecommend = ref(false)
|
||||
|
||||
// 海报图片加载状态
|
||||
const heroImageLoaded = ref(false)
|
||||
// 动态判断当前海报是否为横版(基于图片真实宽高比)
|
||||
const isHeroWideDynamic = ref(false)
|
||||
let heroImagePreloadTimer = null
|
||||
|
||||
// 当前用户信息
|
||||
@ -1701,13 +1747,113 @@ async function handleOpenLogDialog() {
|
||||
logPagination.pageNo = 1
|
||||
logPagination.pageSize = 20
|
||||
logPagination.total = 0
|
||||
logPagination.hasMore = true
|
||||
logGoToPage.value = 1
|
||||
|
||||
// 清空之前的日志
|
||||
rawData.value.loginLogs = []
|
||||
|
||||
// 加载第一页数据
|
||||
await loadMoreLogs()
|
||||
await loadLogPage(true)
|
||||
}
|
||||
|
||||
// 计算总页数
|
||||
const logTotalPages = computed(() => {
|
||||
if (logPagination.total === 0) return 1
|
||||
return Math.ceil(logPagination.total / logPagination.pageSize)
|
||||
})
|
||||
|
||||
// 加载指定页数据(重置模式会清空现有数据)
|
||||
async function loadLogPage(reset = false) {
|
||||
if (loadingLogs.value) return
|
||||
|
||||
loadingLogs.value = true
|
||||
|
||||
try {
|
||||
const params = {
|
||||
pageNo: logPagination.pageNo,
|
||||
pageSize: logPagination.pageSize
|
||||
}
|
||||
|
||||
// 只有管理员(userType=2)才能查询所有日志
|
||||
if (currentUser.userType !== 2) {
|
||||
params.userId = currentUser.userId
|
||||
}
|
||||
|
||||
// 添加筛选条件
|
||||
if (logFilter.username.trim()) {
|
||||
params.username = logFilter.username.trim()
|
||||
}
|
||||
if (logFilter.result) {
|
||||
params.result = logFilter.result
|
||||
}
|
||||
if (logFilter.startDate) {
|
||||
params.startDate = logFilter.startDate
|
||||
}
|
||||
if (logFilter.endDate) {
|
||||
params.endDate = logFilter.endDate
|
||||
}
|
||||
|
||||
const logsRes = await fetchLoginLogs(params)
|
||||
|
||||
if (Array.isArray(logsRes?.data?.list)) {
|
||||
const newLogs = logsRes.data.list
|
||||
|
||||
// 如果是重置模式,替换数据;否则追加
|
||||
if (reset) {
|
||||
if (rawData.value?.loginLogs) {
|
||||
rawData.value.loginLogs = newLogs
|
||||
} else {
|
||||
rawData.value.loginLogs = newLogs
|
||||
}
|
||||
} else {
|
||||
if (rawData.value?.loginLogs) {
|
||||
rawData.value.loginLogs.push(...newLogs)
|
||||
} else {
|
||||
rawData.value.loginLogs = newLogs
|
||||
}
|
||||
}
|
||||
|
||||
// 更新分页信息
|
||||
logPagination.total = logsRes.data.total || 0
|
||||
|
||||
console.log(`成功加载第 ${logPagination.pageNo} 页,共 ${newLogs.length} 条日志,总计 ${logPagination.total} 条`)
|
||||
} else {
|
||||
console.warn('登录日志返回数据异常', logsRes)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载登录日志失败', err)
|
||||
setErrorMessage(err?.message || '加载登录日志失败')
|
||||
} finally {
|
||||
loadingLogs.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理页码变化(从下拉框或翻页按钮触发)
|
||||
async function handleLogPageChange() {
|
||||
// 更新总页数
|
||||
logPagination.pageNo = 1
|
||||
logGoToPage.value = 1
|
||||
rawData.value.loginLogs = []
|
||||
await loadLogPage(true)
|
||||
}
|
||||
|
||||
// 跳转到指定页
|
||||
async function handleLogPageChangeTo(page) {
|
||||
const totalPages = logTotalPages.value
|
||||
if (page < 1 || page > totalPages) return
|
||||
logPagination.pageNo = page
|
||||
logGoToPage.value = page
|
||||
rawData.value.loginLogs = []
|
||||
await loadLogPage(true)
|
||||
}
|
||||
|
||||
// 前往指定页(输入框)
|
||||
async function handleGoToPage() {
|
||||
const totalPages = logTotalPages.value
|
||||
const targetPage = Math.max(1, Math.min(logGoToPage.value || 1, totalPages))
|
||||
if (targetPage === logPagination.pageNo) return
|
||||
logGoToPage.value = targetPage
|
||||
await handleLogPageChangeTo(targetPage)
|
||||
}
|
||||
|
||||
// 加载更多日志(分页加载)
|
||||
@ -2698,15 +2844,30 @@ function preloadImage(url) {
|
||||
|
||||
const img = new Image()
|
||||
img.onload = () => {
|
||||
// 根据真实宽高比判断是否为横版海报(宽高比 > 1.2 视为横版)
|
||||
const aspectRatio = img.naturalWidth / img.naturalHeight
|
||||
isHeroWideDynamic.value = aspectRatio > 1.2
|
||||
heroImageLoaded.value = true
|
||||
}
|
||||
img.onerror = () => {
|
||||
// 图片加载失败,显示默认海报
|
||||
isHeroWideDynamic.value = false
|
||||
heroImageLoaded.value = true
|
||||
}
|
||||
img.src = url
|
||||
}
|
||||
|
||||
// 处理海报图片加载完成事件
|
||||
function handleHeroImageLoad(event) {
|
||||
const img = event.target
|
||||
if (img && img.naturalWidth && img.naturalHeight) {
|
||||
// 根据真实宽高比判断是否为横版海报(宽高比 > 1.2 视为横版)
|
||||
const aspectRatio = img.naturalWidth / img.naturalHeight
|
||||
isHeroWideDynamic.value = aspectRatio > 1.2
|
||||
}
|
||||
heroImageLoaded.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新当前网盘文件夹列表
|
||||
*/
|
||||
|
||||
947
src/style.css
947
src/style.css
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user