diff --git a/src/components/Journal/MiniJournalCard.tsx b/src/components/Journal/MiniJournalCard.tsx
index ae24ab9..0a5a404 100644
--- a/src/components/Journal/MiniJournalCard.tsx
+++ b/src/components/Journal/MiniJournalCard.tsx
@@ -1,15 +1,19 @@
import Image from 'next/image';
import Link from 'next/link';
+import { HighlightText } from '@/components';
+
export default function MiniJournalCard({
image,
journalNo,
title,
+ keyword,
onClick,
}: {
image: string;
journalNo: string;
title: string;
+ keyword: string;
onClick?: () => void;
}) {
return (
@@ -23,8 +27,16 @@ export default function MiniJournalCard({
className="w-162px h-100px rounded-3px group-hover:transform-scale-105 transition-transform-300"
unoptimized
/>
-
{`vol${journalNo.toString().padStart(4, '0')}`}
- {title}
+
+
);
diff --git a/src/components/Journal/MiniJournalCardList.tsx b/src/components/Journal/MiniJournalCardList.tsx
index d17ec17..ce999e2 100644
--- a/src/components/Journal/MiniJournalCardList.tsx
+++ b/src/components/Journal/MiniJournalCardList.tsx
@@ -2,11 +2,12 @@ import MiniJournalCard from './MiniJournalCard';
interface IProps {
list: JournalInfo[];
+ keyword: string;
className?: string;
onClick: () => void;
}
-export default function MiniJournalCardList({ list, className, onClick }: IProps) {
+export default function MiniJournalCardList({ list, keyword, className, onClick }: IProps) {
return (
{list.map((item) => (
@@ -15,6 +16,7 @@ export default function MiniJournalCardList({ list, className, onClick }: IProps
image={item.image}
journalNo={item.journalNo}
title={item.title}
+ keyword={keyword}
onClick={onClick}
/>
))}
diff --git a/src/components/Search/Dropdown.tsx b/src/components/Search/Dropdown.tsx
index 17e47f0..4013ab1 100644
--- a/src/components/Search/Dropdown.tsx
+++ b/src/components/Search/Dropdown.tsx
@@ -5,12 +5,13 @@ import Single from './widget/Single';
interface IProps {
journalData: JournalInfo[];
+ keyword: string;
songData: SongInfo[];
className?: string;
onClose: () => void;
}
-export default function SearchDropdown({ journalData, songData, className, onClose }: IProps) {
+export default function SearchDropdown({ journalData, keyword, songData, className, onClose }: IProps) {
const haveData = journalData.length || songData.length;
return (
@@ -20,8 +21,8 @@ export default function SearchDropdown({ journalData, songData, className, onClo
>
{haveData ? (
- {!!songData.length && }
- {!!journalData.length && }
+ {!!songData.length && }
+ {!!journalData.length && }
) : (
diff --git a/src/components/Search/Search.tsx b/src/components/Search/Search.tsx
index de752a9..f9553d6 100644
--- a/src/components/Search/Search.tsx
+++ b/src/components/Search/Search.tsx
@@ -14,18 +14,18 @@ interface IProps {
}
export default function Search({ className }: IProps) {
- const [value, setValue] = useState
('');
+ const [keyword, setKeyword] = useState('');
const [journalData, setJournalData] = useState([]);
const [songData, setSongData] = useState([]);
const [showDropDown, setShowDropDown] = useState(false);
const searchRef = useRef(null);
- const handleInput: (value: string) => void = (value) => {
- setShowDropDown(!!value.trim());
+ const handleInput: (keyword: string) => void = (keyword) => {
+ setShowDropDown(!!keyword.trim());
- if (value.trim()) {
- setValue(value);
- handleSearch(value);
+ if (keyword.trim()) {
+ setKeyword(keyword);
+ handleSearch(keyword);
}
};
@@ -50,7 +50,7 @@ export default function Search({ className }: IProps) {
// input 聚焦时,展示下拉框
const handleInputFocus = () => {
- if (value.trim()) setShowDropDown(true);
+ if (keyword.trim()) setShowDropDown(true);
};
const handleClickOutside = (e: Event) => {
@@ -68,10 +68,15 @@ export default function Search({ className }: IProps) {
return (
- handleInput(value), 200)} onFocus={handleInputFocus} />
+ handleInput(keyword), 200)} onFocus={handleInputFocus} />
{showDropDown && (
- setShowDropDown(false)} />
+ setShowDropDown(false)}
+ />
)}
);
diff --git a/src/components/Search/widget/Journal.tsx b/src/components/Search/widget/Journal.tsx
index 477cc40..d2aef19 100644
--- a/src/components/Search/widget/Journal.tsx
+++ b/src/components/Search/widget/Journal.tsx
@@ -2,15 +2,16 @@ import { MiniJournalCardList } from '@/components';
interface IProps {
data: JournalInfo[];
+ keyword: string;
className?: string;
onClose: () => void;
}
-export default function Journal({ data, className, onClose }: IProps) {
+export default function Journal({ data, keyword, className, onClose }: IProps) {
return (
);
}
diff --git a/src/components/Search/widget/Single.tsx b/src/components/Search/widget/Single.tsx
index b5a5c1c..def0f51 100644
--- a/src/components/Search/widget/Single.tsx
+++ b/src/components/Search/widget/Single.tsx
@@ -2,19 +2,21 @@ import { SongCardList } from '@/components';
interface IProps {
data: SongInfo[];
+ keyword: string;
className?: string;
}
-export default function Single({ data, className }: IProps) {
+export default function Single({ data, keyword, className }: IProps) {
return (
);
diff --git a/src/components/SongCard/SongCard.tsx b/src/components/SongCard/SongCard.tsx
index 142b764..a292c9b 100644
--- a/src/components/SongCard/SongCard.tsx
+++ b/src/components/SongCard/SongCard.tsx
@@ -1,6 +1,6 @@
import Image from 'next/image';
-import { CollectButton, IconEqualizer } from '@/components';
+import { CollectButton, IconEqualizer, HighlightText } from '@/components';
interface Props extends SongInfo {
/** 播放状态 */
@@ -9,36 +9,53 @@ interface Props extends SongInfo {
showCollect: boolean;
/** 显示均衡器效果 */
showEq: boolean;
+ keyword: string;
onPlay: (id: string) => void;
}
-export default function SongCard({ playState, id, title, pic, artist, haveCollect, duration, showEq, onPlay }: Props) {
+export default function SongCard({
+ playState,
+ id,
+ title,
+ pic,
+ artist,
+ album,
+ showCollect,
+ haveCollect,
+ duration,
+ showEq,
+ keyword,
+ onPlay,
+}: Props) {
return (
onPlay(id)}
>
{/* left */}
-
+
{/* 专辑封面 */}
+
{/* 歌曲名称/歌手 */}
-
-
- {title}
-
-
- {artist}
-
+
+
+
@@ -48,17 +65,17 @@ export default function SongCard({ playState, id, title, pic, artist, haveCollec
{showEq &&
}
{/* 音频时长 */}
-
{duration || '00:00'}
+
{duration || '00:00'}
{/* 收藏按钮单曲 */}
- {
+ {showCollect && (
- }
+ )}
);
diff --git a/src/components/SongCard/SongCardList.tsx b/src/components/SongCard/SongCardList.tsx
index 08781e2..b258371 100644
--- a/src/components/SongCard/SongCardList.tsx
+++ b/src/components/SongCard/SongCardList.tsx
@@ -16,6 +16,8 @@ interface Props {
* playerCard: 播放器内
*/
listInfo: { type: 'vol' | 'collectSingle' | 'playerCard'; id: string | null };
+ // 高亮关键词
+ keyword: string;
/**
* @description 点击单曲事件
* playList: 播放当前列表
@@ -23,8 +25,8 @@ interface Props {
*/
clickType?: 'playList' | 'playPush';
songList: SongInfo[];
- /** 收藏按钮的显示逻辑 always: 总是显示 playing: 播放时显示 */
- collectType?: 'always' | 'playing';
+ /** 收藏按钮的展示逻辑 always: 总是展示 playing: 播放时展示; none: 不展示*/
+ collectType?: 'always' | 'playing' | 'none';
className?: string;
}
@@ -32,6 +34,7 @@ export default function SongCardList({
listInfo,
songList,
className,
+ keyword,
collectType = 'always',
clickType = 'playList',
}: Props) {
@@ -113,7 +116,9 @@ export default function SongCardList({
playState={playState}
showEq={item.id === audioId}
onPlay={(audioId: string) => handlePlay(audioId)}
- showCollect={collectType === 'always' || (collectType === 'playing' && item.id === audioId)}
+ keyword={keyword}
+ // showCollect={collectType === 'always' || (collectType === 'playing' && item.id === audioId)}
+ showCollect={collectType === 'always' || collectType === 'playing'}
/>
))}
diff --git a/src/components/common/HighlightText.tsx b/src/components/common/HighlightText.tsx
new file mode 100644
index 0000000..e6f2ce0
--- /dev/null
+++ b/src/components/common/HighlightText.tsx
@@ -0,0 +1,29 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+interface Iprops {
+ text: string;
+ keyword: string;
+ className?: string;
+}
+
+const HighlightText = ({ text, keyword, className }: Iprops) => {
+ // 使用useState钩子来存储处理后的文本
+ const [highlightedText, setHighlightedText] = useState(text);
+
+ useEffect(() => {
+ if (!keyword) return;
+ // 创建一个正则表达式,全局匹配并忽略大小写
+ const regex = new RegExp(keyword, 'gi');
+ // 使用replace方法替换所有匹配的关键字,并用span标签包裹起来
+ const newText = text.replace(regex, `$&`);
+ // 更新状态以触发组件重新渲染
+ setHighlightedText(newText);
+ }, []); // 仅在text或keyword变化时重新执行effect
+
+ // 渲染带有高亮的文本
+ return ;
+};
+
+export default HighlightText;
diff --git a/src/components/index.ts b/src/components/index.ts
index a3cfa6d..66fab27 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -64,3 +64,4 @@ export { default as QRCodeDialog } from './common/QRCodeDialog';
export { default as InfiniteScroller } from './common/InfiniteScroller';
export { default as Pagination } from './Pagination/Pagination';
export { default as Search } from './Search/Search';
+export { default as HighlightText } from './common/HighlightText';