更新内容
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { BookOpen, ChevronDown, Layers3 } from 'lucide-react';
|
import { ArrowLeft, BookOpen, Layers3 } from 'lucide-react';
|
||||||
import { apiUrl } from '../api';
|
import { apiUrl } from '../api';
|
||||||
|
|
||||||
interface VocabEntry {
|
interface VocabEntry {
|
||||||
@@ -38,7 +38,7 @@ const colorClassFor = (color: string) => {
|
|||||||
|
|
||||||
export const VocabCards: React.FC = () => {
|
export const VocabCards: React.FC = () => {
|
||||||
const [cards, setCards] = useState<VocabCard[]>([]);
|
const [cards, setCards] = useState<VocabCard[]>([]);
|
||||||
const [expandedTerm, setExpandedTerm] = useState<string | null>(null);
|
const [selectedCard, setSelectedCard] = useState<VocabCard | null>(null);
|
||||||
const [status, setStatus] = useState<'loading' | 'ready' | 'error'>('loading');
|
const [status, setStatus] = useState<'loading' | 'ready' | 'error'>('loading');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -68,20 +68,33 @@ export const VocabCards: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 overflow-y-auto bg-[#FAF9F6] px-2 py-3 lg:px-6 lg:pb-6">
|
<div className="flex-1 overflow-y-auto bg-[#FAF9F6] px-2 py-3 lg:px-6 lg:pb-6">
|
||||||
<div className="mx-auto max-w-4xl">
|
<div className="mx-auto max-w-5xl">
|
||||||
<div className="mb-3 flex items-center justify-between gap-3 rounded-xl border border-[#E8E4DE] bg-[#FDFCFB] px-4 py-3 shadow-sm">
|
<div className="mb-3 flex items-center justify-between gap-3 rounded-xl border border-[#E8E4DE] bg-[#FDFCFB] px-4 py-3 shadow-sm">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2 font-serif text-lg font-bold text-[#2D2926]">
|
<div className="flex items-center gap-2 font-serif text-lg font-bold text-[#2D2926]">
|
||||||
<Layers3 className="h-5 w-5 text-[#8B7E74]" />
|
<Layers3 className="h-5 w-5 text-[#8B7E74]" />
|
||||||
单词卡片
|
{selectedCard ? selectedCard.text : '单词卡片'}
|
||||||
</div>
|
</div>
|
||||||
|
{!selectedCard && (
|
||||||
<div className="mt-1 text-xs text-[#A69F92]">
|
<div className="mt-1 text-xs text-[#A69F92]">
|
||||||
按词合并所有歌词注释
|
按词合并所有歌词注释
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{selectedCard ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setSelectedCard(null)}
|
||||||
|
className="flex items-center gap-1 rounded-full border border-[#E8E4DE] bg-white px-3 py-1.5 text-xs font-semibold text-[#8B7E74] hover:bg-[#FAF9F6]"
|
||||||
|
>
|
||||||
|
<ArrowLeft className="h-3.5 w-3.5" />
|
||||||
|
返回
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
<div className="rounded-full bg-[#8B7E74]/10 px-3 py-1 text-xs font-semibold text-[#8B7E74]">
|
<div className="rounded-full bg-[#8B7E74]/10 px-3 py-1 text-xs font-semibold text-[#8B7E74]">
|
||||||
{cards.length} 词
|
{cards.length} 词
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{status === 'loading' && (
|
{status === 'loading' && (
|
||||||
@@ -102,65 +115,48 @@ export const VocabCards: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{status === 'ready' && selectedCard && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className={`rounded-xl border p-3 text-sm leading-relaxed ${colorClassFor(selectedCard.latestColor)}`}>
|
||||||
|
{selectedCard.latestComment || '无释义内容'}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{cards.map((card) => {
|
{selectedCard.entries.map((entry, index) => (
|
||||||
const isExpanded = expandedTerm === card.normalizedText;
|
|
||||||
return (
|
|
||||||
<article
|
|
||||||
key={card.normalizedText}
|
|
||||||
className="rounded-xl border border-[#E8E4DE] bg-[#FDFCFB] p-3 shadow-sm"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setExpandedTerm(isExpanded ? null : card.normalizedText)}
|
|
||||||
className="flex w-full items-start justify-between gap-3 text-left"
|
|
||||||
>
|
|
||||||
<div className="min-w-0">
|
|
||||||
<div className="font-serif text-xl font-bold text-[#2D2926]">
|
|
||||||
{card.text}
|
|
||||||
</div>
|
|
||||||
<div className="mt-1 flex flex-wrap items-center gap-1.5 text-[11px] text-[#A69F92]">
|
|
||||||
<span>{card.noteCount} 条注释</span>
|
|
||||||
<span>·</span>
|
|
||||||
<span className="truncate">{card.documentTitles.join(' / ')}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ChevronDown
|
|
||||||
className={`mt-1 h-4 w-4 shrink-0 text-[#8B7E74] transition ${
|
|
||||||
isExpanded ? 'rotate-180' : ''
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div className={`mt-2 rounded-lg border p-2 text-sm leading-relaxed ${colorClassFor(card.latestColor)}`}>
|
|
||||||
{card.latestComment || '无释义内容'}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isExpanded && (
|
|
||||||
<div className="mt-3 space-y-2 border-t border-[#E8E4DE]/60 pt-3">
|
|
||||||
{card.entries.map((entry, index) => (
|
|
||||||
<div
|
<div
|
||||||
key={`${entry.fileName}-${entry.lineNumber}-${index}`}
|
key={`${entry.fileName}-${entry.lineNumber}-${index}`}
|
||||||
className="rounded-lg border border-[#E8E4DE]/70 bg-white p-2.5"
|
className="rounded-xl border border-[#E8E4DE]/70 bg-white p-3 shadow-sm"
|
||||||
>
|
>
|
||||||
<div className="mb-1 flex items-center gap-1.5 text-[11px] font-semibold text-[#8B7E74]">
|
<div className="mb-1 flex items-center gap-1.5 text-xs font-semibold text-[#8B7E74]">
|
||||||
<BookOpen className="h-3.5 w-3.5" />
|
<BookOpen className="h-3.5 w-3.5" />
|
||||||
{entry.documentTitle} · 行 {entry.lineNumber}
|
{entry.documentTitle} · 行 {entry.lineNumber}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs leading-relaxed text-[#A69F92]">
|
<div className="text-sm leading-relaxed text-[#2D2926]">
|
||||||
{entry.lineText}
|
{entry.lineText}
|
||||||
</div>
|
</div>
|
||||||
<div className={`mt-2 rounded border px-2 py-1.5 text-sm ${colorClassFor(entry.color)}`}>
|
<div className={`mt-2 rounded-lg border px-2 py-1.5 text-sm ${colorClassFor(entry.color)}`}>
|
||||||
{entry.comment}
|
{entry.comment}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</article>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{status === 'ready' && !selectedCard && (
|
||||||
|
<div className="grid grid-cols-3 gap-1.5 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 xl:grid-cols-7">
|
||||||
|
{cards.map((card) => (
|
||||||
|
<button
|
||||||
|
key={card.normalizedText}
|
||||||
|
type="button"
|
||||||
|
onClick={() => setSelectedCard(card)}
|
||||||
|
className="min-h-14 rounded-xl border border-[#E8E4DE] bg-[#FDFCFB] px-2 py-2 text-center font-serif text-base font-bold leading-snug text-[#2D2926] shadow-sm transition hover:border-[#8B7E74]/50 hover:bg-white active:bg-[#E8E4DE]/30 sm:text-lg"
|
||||||
|
>
|
||||||
|
{card.text}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user