feat(auto): 已收藏显示
This commit is contained in:
parent
dc23a4abd7
commit
db1208b981
@ -334,6 +334,7 @@ body:has(.reader-mode) #toast {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: var(--bg-hover);
|
background: var(--bg-hover);
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.novel-cover img {
|
.novel-cover img {
|
||||||
@ -342,6 +343,111 @@ body:has(.reader-mode) #toast {
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 书架标识徽章 - 更醒目美观的版本 */
|
||||||
|
.shelf-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
min-width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 8px;
|
||||||
|
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
|
||||||
|
border-radius: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
box-shadow:
|
||||||
|
0 2px 12px rgba(76, 175, 80, 0.4),
|
||||||
|
0 0 0 2px rgba(255, 255, 255, 0.1),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||||
|
z-index: 10;
|
||||||
|
animation: shelfBadgePulse 2.5s ease-in-out infinite;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
will-change: transform, box-shadow;
|
||||||
|
transform: translateZ(0); /* 启用硬件加速 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.shelf-badge::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: -2px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: linear-gradient(135deg, rgba(76, 175, 80, 0.3), rgba(69, 160, 73, 0.3));
|
||||||
|
z-index: -1;
|
||||||
|
animation: shelfBadgeGlow 2.5s ease-in-out infinite;
|
||||||
|
will-change: opacity, transform;
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shelf-badge svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
fill: #ffffff;
|
||||||
|
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3));
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shelf-badge-text {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
white-space: nowrap;
|
||||||
|
display: none; /* 默认隐藏文字,只显示图标 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表卡片中的书架标识 - 显示文字 */
|
||||||
|
.novel-card .shelf-badge {
|
||||||
|
min-width: auto;
|
||||||
|
padding: 0 6px;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.novel-card .shelf-badge-text {
|
||||||
|
display: inline;
|
||||||
|
font-size: 10px;
|
||||||
|
padding-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shelfBadgePulse {
|
||||||
|
0%, 100% {
|
||||||
|
transform: scale(1) translateZ(0);
|
||||||
|
box-shadow:
|
||||||
|
0 2px 12px rgba(76, 175, 80, 0.4),
|
||||||
|
0 0 0 2px rgba(255, 255, 255, 0.1),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.05) translateZ(0);
|
||||||
|
box-shadow:
|
||||||
|
0 4px 16px rgba(76, 175, 80, 0.5),
|
||||||
|
0 0 0 3px rgba(255, 255, 255, 0.15),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shelfBadgeGlow {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0.5;
|
||||||
|
transform: scale(1) translateZ(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(1.1) translateZ(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 减少动画以节省电量(可选,用户可启用) */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.shelf-badge {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
.shelf-badge::before {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.novel-info {
|
.novel-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@ -411,6 +517,34 @@ body:has(.reader-mode) #toast {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 网格卡片中的书架标识 */
|
||||||
|
.novel-grid-card .shelf-badge {
|
||||||
|
top: 6px;
|
||||||
|
right: 6px;
|
||||||
|
min-width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 6px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.novel-grid-card .shelf-badge::before {
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.novel-grid-card .shelf-badge svg {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 鼠标悬停时的增强效果(桌面端) */
|
||||||
|
@media (hover: hover) {
|
||||||
|
.novel-card:hover .shelf-badge,
|
||||||
|
.novel-grid-card:hover .shelf-badge {
|
||||||
|
animation: shelfBadgePulse 1s ease-in-out infinite;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.novel-grid-card .novel-info {
|
.novel-grid-card .novel-info {
|
||||||
padding: var(--spacing-sm);
|
padding: var(--spacing-sm);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -350,6 +350,9 @@ const App = (function() {
|
|||||||
$btn.find('svg path').attr('d', 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z');
|
$btn.find('svg path').attr('d', 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z');
|
||||||
$btn.contents().last()[0].textContent = '加入书架';
|
$btn.contents().last()[0].textContent = '加入书架';
|
||||||
Toast.show('已从书架移除');
|
Toast.show('已从书架移除');
|
||||||
|
|
||||||
|
// 更新当前页面所有相关书籍卡片的书架标识
|
||||||
|
$(`.novel-card[data-novel-id="${novelId}"] .shelf-badge, .novel-grid-card[data-novel-id="${novelId}"] .shelf-badge`).remove();
|
||||||
} else {
|
} else {
|
||||||
await API.addToShelf({ userId: Store.getUserId(), novelId });
|
await API.addToShelf({ userId: Store.getUserId(), novelId });
|
||||||
Store.addToShelfCache(novelId);
|
Store.addToShelfCache(novelId);
|
||||||
@ -357,6 +360,13 @@ const App = (function() {
|
|||||||
$btn.find('svg path').attr('d', 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z');
|
$btn.find('svg path').attr('d', 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z');
|
||||||
$btn.contents().last()[0].textContent = '已在书架';
|
$btn.contents().last()[0].textContent = '已在书架';
|
||||||
Toast.show('已加入书架');
|
Toast.show('已加入书架');
|
||||||
|
|
||||||
|
// 更新当前页面所有相关书籍卡片的书架标识
|
||||||
|
// 列表视图显示文字,网格视图只显示图标
|
||||||
|
const badgeHtmlList = '<div class="shelf-badge" title="已在书架"><svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg><span class="shelf-badge-text">已收藏</span></div>';
|
||||||
|
const badgeHtmlGrid = '<div class="shelf-badge" title="已在书架"><svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg></div>';
|
||||||
|
$(`.novel-card[data-novel-id="${novelId}"] .novel-cover`).append(badgeHtmlList);
|
||||||
|
$(`.novel-grid-card[data-novel-id="${novelId}"] .novel-cover`).append(badgeHtmlGrid);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Toast.show('操作失败');
|
Toast.show('操作失败');
|
||||||
|
|||||||
@ -199,10 +199,12 @@ const Pages = (function() {
|
|||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
novels.forEach(novel => {
|
novels.forEach(novel => {
|
||||||
|
const isInShelf = Store.isInShelfCache(novel.id);
|
||||||
html += `
|
html += `
|
||||||
<div class="novel-grid-card" data-novel-id="${novel.id}">
|
<div class="novel-grid-card" data-novel-id="${novel.id}">
|
||||||
<div class="novel-cover">
|
<div class="novel-cover">
|
||||||
<img src="${novel.cover || ''}" alt="${novel.name}" loading="lazy">
|
<img src="${novel.cover || ''}" alt="${novel.name}" loading="lazy">
|
||||||
|
${isInShelf ? '<div class="shelf-badge" title="已在书架"><svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg></div>' : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="novel-info">
|
<div class="novel-info">
|
||||||
<div class="novel-title text-ellipsis">${novel.name}</div>
|
<div class="novel-title text-ellipsis">${novel.name}</div>
|
||||||
@ -285,7 +287,8 @@ const Pages = (function() {
|
|||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
novels.forEach(novel => {
|
novels.forEach(novel => {
|
||||||
html += renderNovelCard(novel);
|
const isInShelf = Store.isInShelfCache(novel.id);
|
||||||
|
html += renderNovelCard(novel, isInShelf);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (append) {
|
if (append) {
|
||||||
@ -316,13 +319,16 @@ const Pages = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNovelCard(novel) {
|
function renderNovelCard(novel, inShelf = false) {
|
||||||
const synopsis = cleanContent(novel.synopsis).substring(0, 80);
|
const synopsis = cleanContent(novel.synopsis).substring(0, 80);
|
||||||
const tags = (novel.tags || []).slice(0, 2);
|
const tags = (novel.tags || []).slice(0, 2);
|
||||||
|
// 检查书架状态(如果没有传入,则从缓存中检查)
|
||||||
|
const isInShelf = inShelf !== undefined ? inShelf : Store.isInShelfCache(novel.id);
|
||||||
return `
|
return `
|
||||||
<div class="novel-card" data-novel-id="${novel.id}">
|
<div class="novel-card" data-novel-id="${novel.id}">
|
||||||
<div class="novel-cover">
|
<div class="novel-cover">
|
||||||
<img src="${novel.cover || ''}" alt="${novel.name}" loading="lazy">
|
<img src="${novel.cover || ''}" alt="${novel.name}" loading="lazy">
|
||||||
|
${isInShelf ? '<div class="shelf-badge" title="已在书架"><svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg><span class="shelf-badge-text">已收藏</span></div>' : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="novel-info">
|
<div class="novel-info">
|
||||||
<div class="novel-title text-ellipsis">${novel.name}</div>
|
<div class="novel-title text-ellipsis">${novel.name}</div>
|
||||||
@ -656,7 +662,8 @@ const Pages = (function() {
|
|||||||
|
|
||||||
let html = '<div class="novel-list">';
|
let html = '<div class="novel-list">';
|
||||||
novels.forEach(novel => {
|
novels.forEach(novel => {
|
||||||
html += renderNovelCard(novel);
|
const isInShelf = Store.isInShelfCache(novel.id);
|
||||||
|
html += renderNovelCard(novel, isInShelf);
|
||||||
});
|
});
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
@ -688,7 +695,8 @@ const Pages = (function() {
|
|||||||
|
|
||||||
let html = '<div class="novel-list">';
|
let html = '<div class="novel-list">';
|
||||||
novels.forEach(novel => {
|
novels.forEach(novel => {
|
||||||
html += renderNovelCard(novel);
|
const isInShelf = Store.isInShelfCache(novel.id);
|
||||||
|
html += renderNovelCard(novel, isInShelf);
|
||||||
});
|
});
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user