|
|
@@ -4,74 +4,331 @@ import {
|
|
|
Image,
|
|
|
Pagination,
|
|
|
Popover,
|
|
|
- type PaginationProps,
|
|
|
+ Spin,
|
|
|
} from 'antd'
|
|
|
import './index.css'
|
|
|
import { DownOutlined } from '@ant-design/icons'
|
|
|
import { useNavigate } from 'react-router-dom'
|
|
|
-const SongListinfo = [
|
|
|
- {
|
|
|
- id: 10,
|
|
|
- src: 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
- title: '深度睡眠 |重度失眠者专用歌单',
|
|
|
- content: 'by 音旧- ',
|
|
|
- },
|
|
|
- {
|
|
|
- id: 1,
|
|
|
- src: 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
- title: '深度睡眠 |重度失眠者专用歌单',
|
|
|
- content: 'by 音旧- ',
|
|
|
- },
|
|
|
- {
|
|
|
- id: 2,
|
|
|
- src: 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
- title: '深度睡眠 |重度失眠者专用歌单',
|
|
|
- content: 'by 音旧- ',
|
|
|
- },
|
|
|
- {
|
|
|
- id: 3,
|
|
|
- src: 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
- title: '深度睡眠 |重度失眠者专用歌单',
|
|
|
- content: 'by 音旧- ',
|
|
|
- },
|
|
|
- {
|
|
|
- id: 4,
|
|
|
- src: 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
- title: '深度睡眠 |重度失眠者专用歌单',
|
|
|
- content: 'by 音旧- ',
|
|
|
- },
|
|
|
- {
|
|
|
- id: 5,
|
|
|
- src: 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
- title: '深度睡眠 |重度失眠者专用歌单',
|
|
|
- content: 'by 音旧- ',
|
|
|
- },
|
|
|
- {
|
|
|
- id: 6,
|
|
|
- src: 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
- title: '深度睡眠 |重度失眠者专用歌单',
|
|
|
- content: 'by 音旧- ',
|
|
|
- },
|
|
|
- {
|
|
|
- id: 7,
|
|
|
- src: 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
- title: '深度睡眠 |重度失眠者专用歌单',
|
|
|
- content: 'by 音旧- ',
|
|
|
- },
|
|
|
- {
|
|
|
- id: 8,
|
|
|
- src: 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
- title: '深度睡眠 |重度失眠者专用歌单',
|
|
|
- content: 'by 音旧- ',
|
|
|
- },
|
|
|
- {
|
|
|
- id: 9,
|
|
|
- src: 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
- title: '深度睡眠 |重度失眠者专用歌单',
|
|
|
- content: 'by 音旧- ',
|
|
|
- },
|
|
|
-]
|
|
|
+import { useState, useEffect } from 'react'
|
|
|
+import { getPlaylistPage } from '@/apis/payList'
|
|
|
+import type { PageQuery } from '@/type/PageQuery'
|
|
|
+
|
|
|
+// 定义歌单数据类型
|
|
|
+interface PlaylistItem {
|
|
|
+ id: number;
|
|
|
+ userId: number;
|
|
|
+ playlistName: string;
|
|
|
+ coverUrl: string | null;
|
|
|
+ description: string | null;
|
|
|
+ tag: string | null;
|
|
|
+ songCount: number;
|
|
|
+ playCount: number;
|
|
|
+ createTime: string;
|
|
|
+ updateTime: string;
|
|
|
+ status: boolean;
|
|
|
+}
|
|
|
+
|
|
|
+const SongList = () => {
|
|
|
+ const navigate = useNavigate()
|
|
|
+ const [playlistData, setPlaylistData] = useState<PlaylistItem[]>([])
|
|
|
+ const [loading, setLoading] = useState(false)
|
|
|
+ const [pagination, setPagination] = useState({
|
|
|
+ current: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ total: 0,
|
|
|
+ })
|
|
|
+ // 使用一个对象来存储所有分类的选中状态
|
|
|
+ const [selectedFilters, setSelectedFilters] = useState<Record<string, string[]>>({})
|
|
|
+
|
|
|
+ const fetchPlaylistData = async (pageNo: number, pageSize: number, filters?: Record<string, string>) => {
|
|
|
+ setLoading(true);
|
|
|
+ try {
|
|
|
+ const params: PageQuery = {
|
|
|
+ pageNo,
|
|
|
+ pageSize,
|
|
|
+ };
|
|
|
+
|
|
|
+ // 添加筛选参数
|
|
|
+ if (filters) {
|
|
|
+ Object.keys(filters).forEach(key => {
|
|
|
+ if (filters[key]) {
|
|
|
+ params[key as keyof PageQuery] = filters[key];
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('请求参数:', params); // 调试用
|
|
|
+
|
|
|
+ const response = await getPlaylistPage(params);
|
|
|
+ const { records, total, current, size } = response.data;
|
|
|
|
|
|
+ // 转换数据格式
|
|
|
+ const transformedData = records.map(item => ({
|
|
|
+ id: item.id,
|
|
|
+ src: item.coverUrl || 'http://p1.music.126.net/g2_Gv0dtAicJ3ChTYu28_g==/1393081239628722.jpg?param=140y140',
|
|
|
+ title: item.playlistName,
|
|
|
+ content: `${item.songCount}首`,
|
|
|
+ }));
|
|
|
+
|
|
|
+ setPlaylistData(transformedData);
|
|
|
+
|
|
|
+ // 更新分页信息
|
|
|
+ setPagination({
|
|
|
+ current: current || pageNo,
|
|
|
+ pageSize: size || pageSize,
|
|
|
+ total: total || 0,
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取歌单数据失败:', error);
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 构建筛选参数
|
|
|
+ const buildFilterParams = () => {
|
|
|
+ const params: Record<string, string> = {};
|
|
|
+
|
|
|
+ Object.entries(selectedFilters).forEach(([category, tags]) => {
|
|
|
+ if (tags.length > 0) {
|
|
|
+ // 根据分类映射到后端参数名
|
|
|
+ let paramName = '';
|
|
|
+ switch (category) {
|
|
|
+ case '语种':
|
|
|
+ paramName = 'language';
|
|
|
+ break;
|
|
|
+ case '风格':
|
|
|
+ paramName = 'style';
|
|
|
+ break;
|
|
|
+ case '场景':
|
|
|
+ paramName = 'scene';
|
|
|
+ break;
|
|
|
+ case '情感':
|
|
|
+ paramName = 'emotion';
|
|
|
+ break;
|
|
|
+ case '主题':
|
|
|
+ paramName = 'theme'; // 主题使用theme字段
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ paramName = 'tag'; // 默认使用tag字段
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只取第一个选中的标签,因为后端可能只支持单个值
|
|
|
+ if (tags.length > 0) {
|
|
|
+ params[paramName] = tags[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return params;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 获取选中的标签名称数组
|
|
|
+ const getSelectedTagNames = () => {
|
|
|
+ return Object.values(selectedFilters).flat();
|
|
|
+ };
|
|
|
+
|
|
|
+ // 当筛选条件变化时重新请求数据
|
|
|
+ useEffect(() => {
|
|
|
+ const filterParams = buildFilterParams();
|
|
|
+ fetchPlaylistData(pagination.current, pagination.pageSize, filterParams);
|
|
|
+ }, [selectedFilters]);
|
|
|
+
|
|
|
+ const handlePageChange = (page: number) => {
|
|
|
+ if (pagination.current === page) return;
|
|
|
+ setPagination(prev => ({
|
|
|
+ ...prev,
|
|
|
+ current: page,
|
|
|
+ }));
|
|
|
+ const filterParams = buildFilterParams();
|
|
|
+ fetchPlaylistData(page, pagination.pageSize, filterParams);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理歌单点击事件
|
|
|
+ const handlePlaylistClick = (id: number) => {
|
|
|
+ navigate(`/playlist/${id}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理分类选择
|
|
|
+ const handleCategorySelect = (category: string, tag: string) => {
|
|
|
+ setSelectedFilters(prev => {
|
|
|
+ const currentFilters = { ...prev };
|
|
|
+ const categoryFilters = currentFilters[category] || [];
|
|
|
+
|
|
|
+ // 如果标签已存在,则移除;如果不存在,则添加
|
|
|
+ if (categoryFilters.includes(tag)) {
|
|
|
+ currentFilters[category] = categoryFilters.filter(t => t !== tag);
|
|
|
+ } else {
|
|
|
+ // 每个分类只允许选择一个标签
|
|
|
+ currentFilters[category] = [tag];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果该分类没有选中任何标签,则删除该分类
|
|
|
+ if (currentFilters[category].length === 0) {
|
|
|
+ delete currentFilters[category];
|
|
|
+ }
|
|
|
+
|
|
|
+ return currentFilters;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 重置分页到第一页
|
|
|
+ setPagination(prev => ({ ...prev, current: 1 }));
|
|
|
+ };
|
|
|
+
|
|
|
+ // 检查标签是否被选中
|
|
|
+ const isTagSelected = (category: string, tag: string) => {
|
|
|
+ return selectedFilters[category]?.includes(tag) || false;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 重置特定分类
|
|
|
+ const handleResetCategory = (category: string) => {
|
|
|
+ setSelectedFilters(prev => {
|
|
|
+ const newFilters = { ...prev };
|
|
|
+ delete newFilters[category];
|
|
|
+ return newFilters;
|
|
|
+ });
|
|
|
+
|
|
|
+ setPagination(prev => ({ ...prev, current: 1 }));
|
|
|
+ };
|
|
|
+
|
|
|
+ // 重置所有筛选
|
|
|
+ const handleResetAll = () => {
|
|
|
+ setSelectedFilters({});
|
|
|
+ setPagination(prev => ({ ...prev, current: 1 }));
|
|
|
+ fetchPlaylistData(1, pagination.pageSize, {});
|
|
|
+ };
|
|
|
+
|
|
|
+ const content = (
|
|
|
+ <div className="SongList_dig">
|
|
|
+ {SongList_category.map((item, index) => (
|
|
|
+ <div className="SongList_dig_content" key={index}>
|
|
|
+ <div className="SongList_dig_content_left">
|
|
|
+ <div className="SongList_dig_content_left_image">
|
|
|
+ <Image src={item.icon} preview={false}></Image>
|
|
|
+ </div>
|
|
|
+ <div className="SongList_dig_content_left_title">{item.title}</div>
|
|
|
+ </div>
|
|
|
+ <div className="SongList_dig_content_right">
|
|
|
+ <div className="SongList_dig_content_right_item">
|
|
|
+ {item.data.map((subItem, subIndex) => (
|
|
|
+ <span
|
|
|
+ className={`SongList_dig_content_right_item_item ${isTagSelected(item.title, subItem.title) ? 'active' : ''}`}
|
|
|
+ key={subIndex}
|
|
|
+ onClick={() => handleCategorySelect(item.title, subItem.title)}
|
|
|
+ style={{ cursor: 'pointer' }}
|
|
|
+ >
|
|
|
+ {subItem.title}
|
|
|
+ </span>
|
|
|
+ ))}
|
|
|
+ <Button
|
|
|
+ type="link"
|
|
|
+ size="small"
|
|
|
+ onClick={() => handleResetCategory(item.title)}
|
|
|
+ style={{ marginLeft: 8 }}
|
|
|
+ >
|
|
|
+ 清除
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+
|
|
|
+ // 获取选中的标签名称
|
|
|
+ const selectedTagNames = getSelectedTagNames();
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="songList">
|
|
|
+ <div className="songList_Title">
|
|
|
+ <div className="songList_Title_left">
|
|
|
+ <div className="songList_Title_left_title">
|
|
|
+ {selectedTagNames.length > 0 ? selectedTagNames.join(',') : '全部'}
|
|
|
+ {selectedTagNames.length > 0 && (
|
|
|
+ <Button
|
|
|
+ type="link"
|
|
|
+ size="small"
|
|
|
+ onClick={handleResetAll}
|
|
|
+ style={{ marginLeft: 8 }}
|
|
|
+ >
|
|
|
+ 清除
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <div className="songList_Title_left_select">
|
|
|
+ <Popover
|
|
|
+ content={content}
|
|
|
+ placement="bottomRight"
|
|
|
+ title={<Button>全部风格</Button>}
|
|
|
+ >
|
|
|
+ <Button style={{ color: 'rgb(0, 135, 253)' }}>
|
|
|
+ 选择分类
|
|
|
+ <DownOutlined />
|
|
|
+ </Button>
|
|
|
+ </Popover>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="songList_Title_right">
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ danger
|
|
|
+ onClick={() => handleCategorySelect('风格', '热门')}
|
|
|
+ >
|
|
|
+ 热门
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Spin spinning={loading}>
|
|
|
+ <div className="songList_Content">
|
|
|
+ {playlistData.map((item) => (
|
|
|
+ <div
|
|
|
+ className="songList_Content_body"
|
|
|
+ key={item.id}
|
|
|
+ onClick={() => handlePlaylistClick(item.id)}
|
|
|
+ style={{ cursor: 'pointer' }}
|
|
|
+ >
|
|
|
+ <div className="songList_Content_top">
|
|
|
+ <Image preview={false} width={140} height={140} src={item.src}></Image>
|
|
|
+ </div>
|
|
|
+ <div className="songList_Content_bottom">
|
|
|
+ <div className="songList_Content_bottom_title">
|
|
|
+ {item.title}
|
|
|
+ </div>
|
|
|
+ <div className="songList_Content_bottom_content">
|
|
|
+ {item.content}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ </Spin>
|
|
|
+
|
|
|
+ <div className="songList_list_Pagination">
|
|
|
+ <ConfigProvider
|
|
|
+ theme={{
|
|
|
+ components: {
|
|
|
+ Pagination: {
|
|
|
+ itemActiveBg: 'rgb(255, 0, 0)',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Pagination
|
|
|
+ current={pagination.current}
|
|
|
+ pageSize={pagination.pageSize}
|
|
|
+ total={pagination.total}
|
|
|
+ onChange={handlePageChange}
|
|
|
+ showSizeChanger={false}
|
|
|
+ showTotal={(total, range) => `${range[0]}-${range[1]} 共 ${total} 条`}
|
|
|
+ />
|
|
|
+ </ConfigProvider>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+// 歌单分类数据
|
|
|
const SongList_category = [
|
|
|
{
|
|
|
id: 0,
|
|
|
@@ -89,7 +346,6 @@ const SongList_category = [
|
|
|
id: 1,
|
|
|
icon: 'https://web-asd-asd.oss-cn-beijing.aliyuncs.com/%E9%A3%8E%E6%A0%BC.png',
|
|
|
title: '风格',
|
|
|
- //流行| 摇滚| 民谣| 电子| 舞曲| 说唱| 轻音乐| 爵士| 乡村| R&B/Soul| 古典| 民族| 英伦| 金属| 朋克| 蓝调| 雷鬼| 世界音乐| 拉丁| New Age| 古风| 后摇| Bossa Nova|
|
|
|
data: [
|
|
|
{ id: 5, title: '流行', pid: 1 },
|
|
|
{ id: 6, title: '摇滚', pid: 1 },
|
|
|
@@ -121,7 +377,6 @@ const SongList_category = [
|
|
|
icon: 'https://web-asd-asd.oss-cn-beijing.aliyuncs.com/%E5%9C%BA%E6%99%AF.png',
|
|
|
title: '场景',
|
|
|
data: [
|
|
|
- //清晨| 夜晚| 学习| 工作| 午休| 下午茶| 地铁| 驾车| 运动| 旅行| 散步| 酒吧|
|
|
|
{ id: 0, title: '清晨', pid: 2 },
|
|
|
{ id: 1, title: '夜晚', pid: 2 },
|
|
|
{ id: 2, title: '学习', pid: 2 },
|
|
|
@@ -173,126 +428,13 @@ const SongList_category = [
|
|
|
{ id: 10, title: 'KTV', pid: 4 },
|
|
|
{ id: 11, title: '经典', pid: 4 },
|
|
|
{ id: 12, title: '翻唱', pid: 4 },
|
|
|
- { id: 13, title: '吉他', pid: 4 },
|
|
|
- { id: 14, title: '钢琴', pid: 4 },
|
|
|
- { id: 15, title: '器乐', pid: 4 },
|
|
|
- { id: 16, title: '榜单', pid: 4 },
|
|
|
- { id: 17, title: '00后', pid: 4 },
|
|
|
+ { id: 13, title: '吉他', pid: 13 },
|
|
|
+ { id: 14, title: '钢琴', pid: 14 },
|
|
|
+ { id: 15, title: '器乐', pid: 15 },
|
|
|
+ { id: 16, title: '榜单', pid: 16 },
|
|
|
+ { id: 17, title: '00后', pid: 17 },
|
|
|
],
|
|
|
},
|
|
|
]
|
|
|
|
|
|
-const SongList = () => {
|
|
|
- const navigate = useNavigate() // 使用 useNavigate hook
|
|
|
-
|
|
|
- const itemRender: PaginationProps['itemRender'] = (
|
|
|
- _,
|
|
|
- type,
|
|
|
- originalElement
|
|
|
- ) => {
|
|
|
- if (type === 'prev') {
|
|
|
- return <a style={{ color: 'black' }}>上一页</a>
|
|
|
- }
|
|
|
- if (type === 'next') {
|
|
|
- return <a style={{ color: 'black' }}>下一页</a>
|
|
|
- }
|
|
|
- return originalElement
|
|
|
- }
|
|
|
-
|
|
|
- // 处理歌单点击事件
|
|
|
- const handlePlaylistClick = (id: number) => {
|
|
|
- navigate(`/playlist/${id}`)
|
|
|
- }
|
|
|
-
|
|
|
- const content = (
|
|
|
- <div className="SongList_dig">
|
|
|
- {SongList_category.map((item, index) => (
|
|
|
- <div className="SongList_dig_content" key={index}>
|
|
|
- <div className="SongList_dig_content_left">
|
|
|
- <div className="SongList_dig_content_left_image">
|
|
|
- <Image src={item.icon} preview={false}></Image>
|
|
|
- </div>
|
|
|
- <div className="SongList_dig_content_left_title">{item.title}</div>
|
|
|
- </div>
|
|
|
- <div className="SongList_dig_content_right">
|
|
|
- <div className="SongList_dig_content_right_item">
|
|
|
- {item.data.map((item, index) => (
|
|
|
- <span className='SongList_dig_content_right_item_item' key={index}>{item.title}</span>
|
|
|
- ))}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- ))}
|
|
|
- </div>
|
|
|
- )
|
|
|
-
|
|
|
- return (
|
|
|
- <>
|
|
|
- <div className="songList">
|
|
|
- <div className="songList_Title">
|
|
|
- <div className="songList_Title_left">
|
|
|
- <div className="songList_Title_left_title">全部</div>
|
|
|
- <div className="songList_Title_left_select">
|
|
|
- <Popover
|
|
|
- content={content}
|
|
|
- placement="bottomRight"
|
|
|
- title={<Button>全部风格</Button>}
|
|
|
- >
|
|
|
- <Button style={{ color: 'rgb(0, 135, 253)' }}>
|
|
|
- 选择分类
|
|
|
- <DownOutlined />
|
|
|
- </Button>
|
|
|
- </Popover>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div className="songList_Title_right">
|
|
|
- <Button type="primary" danger>
|
|
|
- 热门
|
|
|
- </Button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div className="songList_Content">
|
|
|
- {SongListinfo.map((item) => (
|
|
|
- <div
|
|
|
- className="songList_Content_body"
|
|
|
- key={item.id}
|
|
|
- onClick={() => handlePlaylistClick(item.id)} // 添加点击事件
|
|
|
- style={{ cursor: 'pointer' }} // 添加鼠标指针样式
|
|
|
- >
|
|
|
- <div className="songList_Content_top">
|
|
|
- <Image preview={false} width={140} src={item.src}></Image>
|
|
|
- </div>
|
|
|
- <div className="songList_Content_bottom">
|
|
|
- <div className="songList_Content_bottom_title">
|
|
|
- {item.title}
|
|
|
- </div>
|
|
|
- <div className="songList_Content_bottom_content">
|
|
|
- {item.content}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- ))}
|
|
|
- </div>
|
|
|
- <div className="songList_list_Pagination">
|
|
|
- <ConfigProvider
|
|
|
- theme={{
|
|
|
- components: {
|
|
|
- Pagination: {
|
|
|
- itemActiveBg: 'rgb(255, 0, 0)',
|
|
|
- },
|
|
|
- },
|
|
|
- }}
|
|
|
- >
|
|
|
- <Pagination
|
|
|
- total={500}
|
|
|
- itemRender={itemRender}
|
|
|
- showSizeChanger={false}
|
|
|
- />
|
|
|
- </ConfigProvider>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </>
|
|
|
- )
|
|
|
-}
|
|
|
-
|
|
|
export default SongList
|