功能:新增电影简介、导演和主演信息展示,优化刮削日志样式
This commit is contained in:
parent
e4a0ac71cc
commit
a9066b9046
@ -513,6 +513,19 @@
|
|||||||
<div v-if="scrapeStatusData.tags" class="scrape-status-tags">
|
<div v-if="scrapeStatusData.tags" class="scrape-status-tags">
|
||||||
<span v-for="tag in (typeof scrapeStatusData.tags === 'string' ? scrapeStatusData.tags.split(',') : scrapeStatusData.tags)" :key="tag">{{ tag }}</span>
|
<span v-for="tag in (typeof scrapeStatusData.tags === 'string' ? scrapeStatusData.tags.split(',') : scrapeStatusData.tags)" :key="tag">{{ tag }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 电影简介 -->
|
||||||
|
<div v-if="scrapeStatusData.overview" class="scrape-status-overview">
|
||||||
|
<span class="scrape-status-label">简介:</span>
|
||||||
|
<span class="scrape-status-value">{{ scrapeStatusData.overview }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- 导演 -->
|
||||||
|
<div v-if="scrapeStatusData.directors" class="scrape-status-meta">
|
||||||
|
<span>导演:{{ scrapeStatusData.directors }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- 主演 -->
|
||||||
|
<div v-if="scrapeStatusData.casts" class="scrape-status-meta">
|
||||||
|
<span>主演:{{ scrapeStatusData.casts }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="scrape-status-loading">
|
<div v-else class="scrape-status-loading">
|
||||||
正在查询刮削状态...
|
正在查询刮削状态...
|
||||||
@ -614,12 +627,23 @@
|
|||||||
<!-- 详细日志(可展开) -->
|
<!-- 详细日志(可展开) -->
|
||||||
<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">
|
<template v-for="(item, idx) in log.detailList" :key="idx">
|
||||||
<span class="detail-item__name">👤 {{ item.username || '用户 ' + (item.userId || idx) }}</span>
|
<div class="detail-item detail-item--user">
|
||||||
<span class="detail-item__status detail-item__status--info">
|
<span class="detail-item__name">👤 {{ item.username || '用户 ' + (item.userId || idx) }}</span>
|
||||||
处理: {{ item.processed || 0 }} | 成功: {{ item.success || 0 }} | 失败: {{ item.failed || 0 }} | 跳过: {{ item.skipped || 0 }}
|
<span class="detail-item__status detail-item__status--info">
|
||||||
</span>
|
处理: {{ item.processed || 0 }} | 成功: {{ item.success || 0 }} | 失败: {{ item.failed || 0 }} | 跳过: {{ item.skipped || 0 }}
|
||||||
</div>
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- 显示具体的消息列表 -->
|
||||||
|
<div v-if="item.messages && item.messages.length > 0" class="detail-item__messages">
|
||||||
|
<div v-for="(msg, msgIdx) in item.messages" :key="msgIdx" class="message-item">
|
||||||
|
<span :class="['message-item__indicator', getMessageStatus(msg)]">
|
||||||
|
{{ getMessageStatus(msg) === 'success' ? '✓' : getMessageStatus(msg) === 'failed' ? '✗' : '⊘' }}
|
||||||
|
</span>
|
||||||
|
<span class="message-item__text">{{ msg }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
@ -1923,10 +1947,11 @@ function openEditRecord(item) {
|
|||||||
editingRecord.value = {
|
editingRecord.value = {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
title: item.title || '',
|
title: item.title || '',
|
||||||
subtitle: item.subtitle || '',
|
|
||||||
originalName: item.originalName || '',
|
originalName: item.originalName || '',
|
||||||
description: item.description || '',
|
description: item.description || '',
|
||||||
category: item.category || '',
|
category: item.category || '',
|
||||||
|
directors: item.directors || '',
|
||||||
|
casts: item.casts || '',
|
||||||
updatedAt: item.updatedAt || '',
|
updatedAt: item.updatedAt || '',
|
||||||
poster: item.poster || '',
|
poster: item.poster || '',
|
||||||
tags: [...(item.tags || [])],
|
tags: [...(item.tags || [])],
|
||||||
@ -2001,12 +2026,13 @@ function handleAddFile(parentId) {
|
|||||||
editingRecord.value = {
|
editingRecord.value = {
|
||||||
id: null,
|
id: null,
|
||||||
title: '',
|
title: '',
|
||||||
subtitle: '',
|
|
||||||
tags: [],
|
tags: [],
|
||||||
score: 0,
|
score: 0,
|
||||||
originalName: '',
|
originalName: '',
|
||||||
description: '',
|
description: '',
|
||||||
category: '',
|
category: '',
|
||||||
|
directors: '',
|
||||||
|
casts: '',
|
||||||
sheetId: activeSheetId.value,
|
sheetId: activeSheetId.value,
|
||||||
parentId: parentId || null,
|
parentId: parentId || null,
|
||||||
type: 'file',
|
type: 'file',
|
||||||
@ -2331,11 +2357,12 @@ async function handleSaveRecord(record) {
|
|||||||
const result = await updateMovieInDrive(driveId, record.id, {
|
const result = await updateMovieInDrive(driveId, record.id, {
|
||||||
title: record.title,
|
title: record.title,
|
||||||
originalName: record.originalName,
|
originalName: record.originalName,
|
||||||
subtitle: record.subtitle,
|
|
||||||
description: record.description,
|
description: record.description,
|
||||||
rating: record.score,
|
rating: record.score,
|
||||||
tags: Array.isArray(record.tags) ? record.tags.join(',') : '',
|
tags: Array.isArray(record.tags) ? record.tags.join(',') : '',
|
||||||
category: record.category,
|
category: record.category,
|
||||||
|
directors: record.directors,
|
||||||
|
casts: record.casts,
|
||||||
})
|
})
|
||||||
|
|
||||||
const updatedData = result?.data || {}
|
const updatedData = result?.data || {}
|
||||||
@ -2343,9 +2370,10 @@ async function handleSaveRecord(record) {
|
|||||||
...record,
|
...record,
|
||||||
title: updatedData.title || record.title,
|
title: updatedData.title || record.title,
|
||||||
originalName: updatedData.originalName || record.originalName,
|
originalName: updatedData.originalName || record.originalName,
|
||||||
subtitle: updatedData.subtitle ?? record.subtitle,
|
|
||||||
description: updatedData.description ?? record.description,
|
description: updatedData.description ?? record.description,
|
||||||
category: updatedData.category ?? record.category,
|
category: updatedData.category ?? record.category,
|
||||||
|
directors: updatedData.directors ?? record.directors,
|
||||||
|
casts: updatedData.casts ?? record.casts,
|
||||||
score: Number(updatedData.rating ?? record.score ?? 0),
|
score: Number(updatedData.rating ?? record.score ?? 0),
|
||||||
tags: updatedData.tags ?? record.tags ?? [],
|
tags: updatedData.tags ?? record.tags ?? [],
|
||||||
updatedAt: updatedData.updateTime || updatedData.createTime || record.updatedAt || new Date().toLocaleString('zh-CN', { hour12: false }),
|
updatedAt: updatedData.updateTime || updatedData.createTime || record.updatedAt || new Date().toLocaleString('zh-CN', { hour12: false }),
|
||||||
@ -2488,7 +2516,6 @@ async function loadRecommendMovies() {
|
|||||||
recommendMovies.value = result.data.list.map(movie => ({
|
recommendMovies.value = result.data.list.map(movie => ({
|
||||||
id: movie.id,
|
id: movie.id,
|
||||||
title: movie.title || '未知标题',
|
title: movie.title || '未知标题',
|
||||||
subtitle: movie.subtitle || '',
|
|
||||||
description: movie.description || '',
|
description: movie.description || '',
|
||||||
posterUrl: movie.posterUrl || '',
|
posterUrl: movie.posterUrl || '',
|
||||||
rating: movie.rating || 0,
|
rating: movie.rating || 0,
|
||||||
@ -2621,6 +2648,9 @@ function handleScrapeStatus(node) {
|
|||||||
rating: data.rating,
|
rating: data.rating,
|
||||||
category: data.category,
|
category: data.category,
|
||||||
tags: data.tags,
|
tags: data.tags,
|
||||||
|
overview: data.overview || '',
|
||||||
|
casts: data.casts || '',
|
||||||
|
directors: data.directors || '',
|
||||||
error: data.error || data.message,
|
error: data.error || data.message,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -2640,10 +2670,37 @@ function getStatusText(status) {
|
|||||||
scraping: '刮削中',
|
scraping: '刮削中',
|
||||||
success: '刮削成功',
|
success: '刮削成功',
|
||||||
failed: '刮削失败',
|
failed: '刮削失败',
|
||||||
|
partial: '部分成功',
|
||||||
|
skipped: '已跳过',
|
||||||
}
|
}
|
||||||
return statusMap[status] || '未知状态'
|
return statusMap[status] || '未知状态'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断刮削消息的状态
|
||||||
|
* @param {string} msg - 消息文本
|
||||||
|
* @returns {string} 'success' | 'failed' | 'skipped'
|
||||||
|
*/
|
||||||
|
function getMessageStatus(msg) {
|
||||||
|
if (!msg) return 'skipped'
|
||||||
|
|
||||||
|
// 成功的标志
|
||||||
|
if (msg.includes('刮削成功') || msg.includes('成功') || msg.includes('Successfully')) {
|
||||||
|
return 'success'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 失败的标志
|
||||||
|
if (msg.includes('刮削失败') || msg.includes('失败') ||
|
||||||
|
msg.includes('未找到') || msg.includes('错误') ||
|
||||||
|
msg.includes('Error') || msg.includes('Failed') ||
|
||||||
|
msg.includes('不存在') || msg.includes('跳过')) {
|
||||||
|
return 'failed'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认视为跳过(如:已存在、无需处理等)
|
||||||
|
return 'skipped'
|
||||||
|
}
|
||||||
|
|
||||||
// 多选模式相关函数
|
// 多选模式相关函数
|
||||||
function toggleMultiSelectMode() {
|
function toggleMultiSelectMode() {
|
||||||
multiSelectMode.value = !multiSelectMode.value
|
multiSelectMode.value = !multiSelectMode.value
|
||||||
|
|||||||
@ -16,10 +16,6 @@
|
|||||||
<span>文件名</span>
|
<span>文件名</span>
|
||||||
<input v-model.trim="localRecord.title" type="text" placeholder="请输入电影文件名,如 xxx.mp4" />
|
<input v-model.trim="localRecord.title" type="text" placeholder="请输入电影文件名,如 xxx.mp4" />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
|
||||||
<span>原始文件名</span>
|
|
||||||
<input v-model.trim="localRecord.originalName" type="text" placeholder="用于 TMDB 刮削纠错" />
|
|
||||||
</label>
|
|
||||||
<label>
|
<label>
|
||||||
<span>所属网盘</span>
|
<span>所属网盘</span>
|
||||||
<select v-model="localRecord.sheetId">
|
<select v-model="localRecord.sheetId">
|
||||||
@ -31,12 +27,12 @@
|
|||||||
<input v-model.number="localRecord.score" type="number" min="0" max="100" placeholder="0-100" />
|
<input v-model.number="localRecord.score" type="number" min="0" max="100" placeholder="0-100" />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>副标题</span>
|
<span>导演</span>
|
||||||
<input v-model.trim="localRecord.subtitle" type="text" placeholder="可选" />
|
<input v-model.trim="localRecord.directors" type="text" placeholder="如 导演1, 导演2" />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>分类标签</span>
|
<span>主演</span>
|
||||||
<input v-model.trim="localRecord.category" type="text" placeholder="如 科幻 / 动作" />
|
<input v-model.trim="localRecord.casts" type="text" placeholder="如 主演1, 主演2" />
|
||||||
</label>
|
</label>
|
||||||
<label class="field-span-2">
|
<label class="field-span-2">
|
||||||
<span>标签</span>
|
<span>标签</span>
|
||||||
@ -98,12 +94,13 @@ function createEmptyRecord() {
|
|||||||
return {
|
return {
|
||||||
id: null,
|
id: null,
|
||||||
title: '',
|
title: '',
|
||||||
subtitle: '',
|
|
||||||
tags: [],
|
tags: [],
|
||||||
score: 0,
|
score: 0,
|
||||||
originalName: '',
|
originalName: '',
|
||||||
description: '',
|
description: '',
|
||||||
category: '',
|
category: '',
|
||||||
|
directors: '',
|
||||||
|
casts: '',
|
||||||
sheetId: props.activeSheetId || props.sheets[0]?.id || '',
|
sheetId: props.activeSheetId || props.sheets[0]?.id || '',
|
||||||
parentId: null,
|
parentId: null,
|
||||||
}
|
}
|
||||||
@ -118,9 +115,10 @@ function handleSave() {
|
|||||||
...localRecord.value,
|
...localRecord.value,
|
||||||
title: localRecord.value.title?.trim() || '',
|
title: localRecord.value.title?.trim() || '',
|
||||||
originalName: localRecord.value.originalName?.trim() || '',
|
originalName: localRecord.value.originalName?.trim() || '',
|
||||||
subtitle: localRecord.value.subtitle?.trim() || '',
|
|
||||||
description: localRecord.value.description?.trim() || '',
|
description: localRecord.value.description?.trim() || '',
|
||||||
category: localRecord.value.category?.trim() || '',
|
category: localRecord.value.category?.trim() || '',
|
||||||
|
directors: localRecord.value.directors?.trim() || '',
|
||||||
|
casts: localRecord.value.casts?.trim() || '',
|
||||||
})
|
})
|
||||||
closeDialog()
|
closeDialog()
|
||||||
}
|
}
|
||||||
|
|||||||
698
vue/src/components/SettingsView.vue
Normal file
698
vue/src/components/SettingsView.vue
Normal file
@ -0,0 +1,698 @@
|
|||||||
|
<template>
|
||||||
|
<div class="settings-view">
|
||||||
|
<div class="settings-header">
|
||||||
|
<h2>设置</h2>
|
||||||
|
<p>配置 OpenList 后端接口,绑定您的云盘账户</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-card">
|
||||||
|
<h3 class="settings-card__title">
|
||||||
|
<span class="settings-card__icon">🔗</span>
|
||||||
|
OpenList 绑定
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<form class="settings-form" @submit.prevent="handleBind">
|
||||||
|
<!-- OpenList 地址 -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="openlist-url">
|
||||||
|
<span class="form-label__icon">🌐</span>
|
||||||
|
<span class="form-label__text">OpenList 地址</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="openlist-url"
|
||||||
|
v-model="formData.url"
|
||||||
|
type="url"
|
||||||
|
class="form-input"
|
||||||
|
placeholder="例如:https://olist.example.com 或 http://localhost:5575"
|
||||||
|
required
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
<span class="form-help">请填写 OpenList 服务的完整访问地址,包含协议头</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 账号 -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="openlist-username">
|
||||||
|
<span class="form-label__icon">👤</span>
|
||||||
|
<span class="form-label__text">账号</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="openlist-username"
|
||||||
|
v-model="formData.username"
|
||||||
|
type="text"
|
||||||
|
class="form-input"
|
||||||
|
placeholder="请输入 OpenList 用户名"
|
||||||
|
required
|
||||||
|
autocomplete="username"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 密码 -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="openlist-password">
|
||||||
|
<span class="form-label__icon">🔒</span>
|
||||||
|
<span class="form-label__text">密码</span>
|
||||||
|
</label>
|
||||||
|
<div class="password-input-wrapper">
|
||||||
|
<input
|
||||||
|
id="openlist-password"
|
||||||
|
v-model="formData.password"
|
||||||
|
:type="showPassword ? 'text' : 'password'"
|
||||||
|
class="form-input"
|
||||||
|
placeholder="请输入 OpenList 密码"
|
||||||
|
required
|
||||||
|
autocomplete="current-password"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="password-toggle"
|
||||||
|
@click="showPassword = !showPassword"
|
||||||
|
:title="showPassword ? '隐藏密码' : '显示密码'"
|
||||||
|
>
|
||||||
|
{{ showPassword ? '🙈' : '👁️' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 绑定按钮 -->
|
||||||
|
<div class="form-actions">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn-bind"
|
||||||
|
:disabled="binding"
|
||||||
|
:class="{ 'btn-bind--loading': binding }"
|
||||||
|
>
|
||||||
|
<span v-if="!binding">🔗 绑定 OpenList</span>
|
||||||
|
<span v-else class="loading-spinner">⏳ 绑定中...</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn-reset"
|
||||||
|
@click="resetForm"
|
||||||
|
:disabled="binding"
|
||||||
|
>
|
||||||
|
重置
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- 绑定状态提示 -->
|
||||||
|
<Transition name="fade">
|
||||||
|
<div v-if="bindStatus" class="bind-status" :class="`bind-status--${bindStatus.type}`">
|
||||||
|
<span class="bind-status__icon">{{ bindStatus.icon }}</span>
|
||||||
|
<span class="bind-status__text">{{ bindStatus.message }}</span>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
|
||||||
|
<!-- 已绑定信息 -->
|
||||||
|
<Transition name="fade">
|
||||||
|
<div v-if="boundInfo" class="bound-info">
|
||||||
|
<div class="bound-info__header">
|
||||||
|
<span class="bound-info__icon">✅</span>
|
||||||
|
<span class="bound-info__text">已绑定 OpenList</span>
|
||||||
|
</div>
|
||||||
|
<div class="bound-info__details">
|
||||||
|
<div class="bound-info__item">
|
||||||
|
<span class="bound-info__label">地址:</span>
|
||||||
|
<span class="bound-info__value">{{ boundInfo.url }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="bound-info__item">
|
||||||
|
<span class="bound-info__label">账号:</span>
|
||||||
|
<span class="bound-info__value">{{ boundInfo.username }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="bound-info__item">
|
||||||
|
<span class="bound-info__label">绑定时间:</span>
|
||||||
|
<span class="bound-info__value">{{ boundInfo.boundAt }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn-unbind"
|
||||||
|
@click="handleUnbind"
|
||||||
|
>
|
||||||
|
🗑️ 解除绑定
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { bindOpenList, unbindOpenList, fetchOpenListStatus } from '../api'
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = reactive({
|
||||||
|
url: '',
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 状态控制
|
||||||
|
const binding = ref(false)
|
||||||
|
const showPassword = ref(false)
|
||||||
|
const bindStatus = ref(null)
|
||||||
|
const boundInfo = ref(null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载已绑定的 OpenList 信息
|
||||||
|
*/
|
||||||
|
async function loadBoundInfo() {
|
||||||
|
try {
|
||||||
|
const result = await fetchOpenListStatus()
|
||||||
|
if (result?.code === 200 && result?.data?.bound) {
|
||||||
|
boundInfo.value = {
|
||||||
|
url: result.data.url || '',
|
||||||
|
username: result.data.username || '',
|
||||||
|
boundAt: result.data.boundAt || '-',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('加载绑定信息失败:', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理绑定表单提交
|
||||||
|
*/
|
||||||
|
async function handleBind() {
|
||||||
|
if (!formData.url || !formData.username || !formData.password) {
|
||||||
|
setBindStatus('error', '请填写所有必填字段')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证 URL 格式
|
||||||
|
try {
|
||||||
|
new URL(formData.url)
|
||||||
|
} catch {
|
||||||
|
setBindStatus('error', '请输入有效的 URL 地址(需包含协议头,如 https://)')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.value = true
|
||||||
|
setBindStatus(null, '')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await bindOpenList({
|
||||||
|
url: formData.url,
|
||||||
|
username: formData.username,
|
||||||
|
password: formData.password,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result?.code === 200) {
|
||||||
|
setBindStatus('success', '绑定成功!')
|
||||||
|
boundInfo.value = {
|
||||||
|
url: formData.url,
|
||||||
|
username: formData.username,
|
||||||
|
boundAt: new Date().toLocaleString('zh-CN'),
|
||||||
|
}
|
||||||
|
// 清空密码字段
|
||||||
|
formData.password = ''
|
||||||
|
// 重新加载绑定信息
|
||||||
|
await loadBoundInfo()
|
||||||
|
} else {
|
||||||
|
setBindStatus('error', result?.message || '绑定失败,请稍后重试')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('绑定 OpenList 失败:', err)
|
||||||
|
const errorMsg = err?.message || err?.payload?.message || '网络错误,请检查后端连接'
|
||||||
|
setBindStatus('error', errorMsg)
|
||||||
|
} finally {
|
||||||
|
binding.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理解除绑定
|
||||||
|
*/
|
||||||
|
async function handleUnbind() {
|
||||||
|
if (!confirm('确定要解除 OpenList 绑定吗?')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await unbindOpenList()
|
||||||
|
if (result?.code === 200) {
|
||||||
|
setBindStatus('success', '已解除绑定')
|
||||||
|
boundInfo.value = null
|
||||||
|
resetForm()
|
||||||
|
setTimeout(() => {
|
||||||
|
setBindStatus(null, '')
|
||||||
|
}, 3000)
|
||||||
|
} else {
|
||||||
|
setBindStatus('error', result?.message || '解除绑定失败')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('解除绑定失败:', err)
|
||||||
|
setBindStatus('error', err?.message || '网络错误')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置表单
|
||||||
|
*/
|
||||||
|
function resetForm() {
|
||||||
|
formData.url = ''
|
||||||
|
formData.username = ''
|
||||||
|
formData.password = ''
|
||||||
|
setBindStatus(null, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置绑定状态提示
|
||||||
|
*/
|
||||||
|
function setBindStatus(type, message) {
|
||||||
|
if (!type) {
|
||||||
|
bindStatus.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
success: '✅',
|
||||||
|
error: '❌',
|
||||||
|
warning: '⚠️',
|
||||||
|
info: 'ℹ️',
|
||||||
|
}
|
||||||
|
|
||||||
|
bindStatus.value = {
|
||||||
|
type,
|
||||||
|
message,
|
||||||
|
icon: icons[type] || '',
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3秒后自动清除
|
||||||
|
if (type !== 'success') {
|
||||||
|
setTimeout(() => {
|
||||||
|
bindStatus.value = null
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时加载已绑定信息
|
||||||
|
onMounted(() => {
|
||||||
|
loadBoundInfo()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.settings-view {
|
||||||
|
max-width: 720px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 24px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-header {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-header h2 {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1a1a;
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-header p {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片样式 */
|
||||||
|
.settings-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-card__title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1a1a;
|
||||||
|
margin: 0 0 24px 0;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-card__icon {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表单样式 */
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label__icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 14px;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 8px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:hover {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:focus {
|
||||||
|
border-color: #409eff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input::placeholder {
|
||||||
|
color: #bfbfbf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-help {
|
||||||
|
display: block;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 密码输入框 */
|
||||||
|
.password-input-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-input-wrapper .form-input {
|
||||||
|
padding-right: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-toggle {
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-toggle:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮组 */
|
||||||
|
.form-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 28px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-bind {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px 24px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #fff;
|
||||||
|
background: linear-gradient(135deg, #409eff, #337ecc);
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-bind:hover:not(:disabled) {
|
||||||
|
background: linear-gradient(135deg, #66b1ff, #409eff);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-bind:active:not(:disabled) {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-bind:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-bind--loading {
|
||||||
|
background: linear-gradient(135deg, #909399, #69696d);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reset {
|
||||||
|
padding: 12px 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reset:hover:not(:disabled) {
|
||||||
|
color: #409eff;
|
||||||
|
border-color: #409eff;
|
||||||
|
background: #ecf5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reset:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态提示 */
|
||||||
|
.bind-status {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-top: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bind-status--success {
|
||||||
|
background: #f0f9ff;
|
||||||
|
border: 1px solid #b3e19c;
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bind-status--error {
|
||||||
|
background: #fef0f0;
|
||||||
|
border: 1px solid #fbc4c4;
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bind-status__icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 已绑定信息 */
|
||||||
|
.bound-info {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #f0f9ff;
|
||||||
|
border: 1px solid #b3e19c;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bound-info__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #67c23a;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bound-info__details {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bound-info__item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bound-info__label {
|
||||||
|
color: #999;
|
||||||
|
min-width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bound-info__value {
|
||||||
|
color: #333;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-unbind {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #f56c6c;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #fbc4c4;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-unbind:hover {
|
||||||
|
background: #fef0f0;
|
||||||
|
border-color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载动画 */
|
||||||
|
.loading-spinner {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 过渡动画 */
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 深色模式适配 */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.settings-header h2 {
|
||||||
|
color: #e5e5e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-header p {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-card {
|
||||||
|
background: #2a2a2a;
|
||||||
|
border-color: #3a3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-card__title {
|
||||||
|
color: #e5e5e5;
|
||||||
|
border-bottom-color: #3a3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
background: #1a1a1a;
|
||||||
|
border-color: #3a3a3a;
|
||||||
|
color: #e5e5e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:hover {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:focus {
|
||||||
|
border-color: #409eff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input::placeholder {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-toggle:hover {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reset {
|
||||||
|
color: #ccc;
|
||||||
|
background: #333;
|
||||||
|
border-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reset:hover:not(:disabled) {
|
||||||
|
color: #409eff;
|
||||||
|
border-color: #409eff;
|
||||||
|
background: #1a2a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bound-info {
|
||||||
|
background: #1a2a3a;
|
||||||
|
border-color: #2a4a5a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bound-info__item {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bound-info__value {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-unbind {
|
||||||
|
color: #f56c6c;
|
||||||
|
background: #2a1a1a;
|
||||||
|
border-color: #4a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-unbind:hover {
|
||||||
|
background: #3a1a1a;
|
||||||
|
border-color: #f56c6c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式 */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.settings-view {
|
||||||
|
padding: 16px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-card {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-bind,
|
||||||
|
.btn-reset {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -886,7 +886,7 @@ canvas {
|
|||||||
|
|
||||||
.hero-carousel {
|
.hero-carousel {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== 主海报横幅 ==================== */
|
/* ==================== 主海报横幅 ==================== */
|
||||||
@ -1625,6 +1625,42 @@ canvas {
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 刮削状态面板 - 新增字段样式 */
|
||||||
|
.scrape-status-overview {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: rgba(99, 102, 241, 0.05);
|
||||||
|
border-radius: 8px;
|
||||||
|
border-left: 3px solid #6366f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrape-status-label {
|
||||||
|
font-weight: 700;
|
||||||
|
color: #6366f1;
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrape-status-value {
|
||||||
|
color: #374151;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrape-status-meta {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrape-status-meta span {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 12px;
|
||||||
|
background: rgba(99, 102, 241, 0.08);
|
||||||
|
border-radius: 6px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.folder-tree-actions__btn {
|
.folder-tree-actions__btn {
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
border: 2px dashed var(--line);
|
border: 2px dashed var(--line);
|
||||||
@ -2285,6 +2321,91 @@ canvas {
|
|||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 用户详情项 */
|
||||||
|
.detail-item--user {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: rgba(249, 250, 251, 0.8);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 消息列表容器 */
|
||||||
|
.detail-item__messages {
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
background: rgba(255, 255, 255, 0.6);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(99, 102, 241, 0.08);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 单条消息项 */
|
||||||
|
.message-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
transition: background 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-item:hover {
|
||||||
|
background: rgba(249, 250, 251, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 消息状态指示器 */
|
||||||
|
.message-item__indicator {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 成功消息 */
|
||||||
|
.message-item__indicator.success {
|
||||||
|
background: rgba(34, 197, 94, 0.12);
|
||||||
|
color: #15803d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 失败消息 */
|
||||||
|
.message-item__indicator.failed {
|
||||||
|
background: rgba(239, 68, 68, 0.12);
|
||||||
|
color: #b91c1c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 跳过/未知消息 */
|
||||||
|
.message-item__indicator.skipped {
|
||||||
|
background: rgba(100, 116, 139, 0.1);
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 消息文本 */
|
||||||
|
.message-item__text {
|
||||||
|
flex: 1;
|
||||||
|
color: #374151;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-item__text:hover {
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
.log-time {
|
.log-time {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
@ -3507,7 +3628,6 @@ canvas {
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
max-height: 300px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrape-status-poster img,
|
.scrape-status-poster img,
|
||||||
@ -3518,8 +3638,22 @@ canvas {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 海报图 - 竖版展示(标准海报比例 2:3) */
|
||||||
.scrape-status-poster {
|
.scrape-status-poster {
|
||||||
max-height: 400px;
|
max-height: 500px;
|
||||||
|
aspect-ratio: 2 / 3;
|
||||||
|
max-width: 280px;
|
||||||
|
margin: 0 auto 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrape-status-poster img {
|
||||||
|
aspect-ratio: 2 / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 背景图 - 横版展示(宽屏比例 16:9) */
|
||||||
|
.scrape-status-backdrop {
|
||||||
|
max-height: 320px;
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrape-status-meta {
|
.scrape-status-meta {
|
||||||
@ -4077,8 +4211,9 @@ body.dark-mode .hero-sub__thumb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.dark-mode .hero-sub__card.active .hero-sub__thumb {
|
body.dark-mode .hero-sub__card.active .hero-sub__thumb {
|
||||||
border-color: #6366f1;
|
/* 移除选中状态下的蓝色光圈 */
|
||||||
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.4), 0 4px 16px rgba(0, 0, 0, 0.4);
|
border-color: rgba(51, 65, 85, 0.5);
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
body.dark-mode .hero-sub__title {
|
body.dark-mode .hero-sub__title {
|
||||||
|
|||||||
@ -8,7 +8,8 @@ export function filterRows(rows, keyword) {
|
|||||||
return rows.filter((item) => {
|
return rows.filter((item) => {
|
||||||
const merged = [
|
const merged = [
|
||||||
item.title,
|
item.title,
|
||||||
item.subtitle,
|
item.directors,
|
||||||
|
item.casts,
|
||||||
item.accountName,
|
item.accountName,
|
||||||
item.owner,
|
item.owner,
|
||||||
item.sheetName,
|
item.sheetName,
|
||||||
@ -135,7 +136,6 @@ export function createRecord(record = {}, fallbackSheetName = '') {
|
|||||||
return {
|
return {
|
||||||
id: record.id || `record-${Date.now()}`,
|
id: record.id || `record-${Date.now()}`,
|
||||||
title: normalizeText(record.title),
|
title: normalizeText(record.title),
|
||||||
subtitle: normalizeText(record.subtitle),
|
|
||||||
accountName: normalizeText(record.accountName),
|
accountName: normalizeText(record.accountName),
|
||||||
owner: normalizeText(record.owner),
|
owner: normalizeText(record.owner),
|
||||||
updatedAt: normalizeText(record.updatedAt),
|
updatedAt: normalizeText(record.updatedAt),
|
||||||
@ -149,6 +149,8 @@ export function createRecord(record = {}, fallbackSheetName = '') {
|
|||||||
originalName: normalizeText(record.originalName),
|
originalName: normalizeText(record.originalName),
|
||||||
description: normalizeText(record.description),
|
description: normalizeText(record.description),
|
||||||
category: normalizeText(record.category),
|
category: normalizeText(record.category),
|
||||||
|
directors: normalizeText(record.directors),
|
||||||
|
casts: normalizeText(record.casts),
|
||||||
driveId: record.driveId ?? null,
|
driveId: record.driveId ?? null,
|
||||||
folderId: record.folderId ?? null,
|
folderId: record.folderId ?? null,
|
||||||
minioUrl: normalizeText(record.minioUrl),
|
minioUrl: normalizeText(record.minioUrl),
|
||||||
@ -163,7 +165,6 @@ export function mapMovieToRecord(movie = {}, fallbackSheetName = '') {
|
|||||||
return createRecord({
|
return createRecord({
|
||||||
id: movie.id,
|
id: movie.id,
|
||||||
title: movie.title,
|
title: movie.title,
|
||||||
subtitle: movie.subtitle || '',
|
|
||||||
accountName: movie.resourceLibrary || '',
|
accountName: movie.resourceLibrary || '',
|
||||||
owner: movie.maintainer || '',
|
owner: movie.maintainer || '',
|
||||||
updatedAt: movie.updateTime || '',
|
updatedAt: movie.updateTime || '',
|
||||||
@ -176,6 +177,8 @@ export function mapMovieToRecord(movie = {}, fallbackSheetName = '') {
|
|||||||
type: 'file',
|
type: 'file',
|
||||||
category: movie.category,
|
category: movie.category,
|
||||||
description: movie.description || '',
|
description: movie.description || '',
|
||||||
|
directors: movie.directors || '',
|
||||||
|
casts: movie.casts || '',
|
||||||
}, fallbackSheetName)
|
}, fallbackSheetName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user