|
@@ -1,10 +1,24 @@
|
|
|
import { Playlist, PlaylistSong } from '../viewmodel/Playlist';
|
|
import { Playlist, PlaylistSong } from '../viewmodel/Playlist';
|
|
|
import { VideoItem } from '../viewmodel/VideoItem';
|
|
import { VideoItem } from '../viewmodel/VideoItem';
|
|
|
import PlaylistTable from '../common/util/PlaylistTable';
|
|
import PlaylistTable from '../common/util/PlaylistTable';
|
|
|
|
|
+import MediaTable from '../common/util/MediaTable';
|
|
|
import { emitter } from '@kit.BasicServicesKit';
|
|
import { emitter } from '@kit.BasicServicesKit';
|
|
|
-import { ToastUtil, AppUtil } from '@pura/harmony-utils';
|
|
|
|
|
|
|
+import { ToastUtil, AppUtil, LogUtil } from '@pura/harmony-utils';
|
|
|
import { showEditPlaylistDialog } from '../dialog/PlaylistDialog';
|
|
import { showEditPlaylistDialog } from '../dialog/PlaylistDialog';
|
|
|
|
|
+import { showAddSongsToPlaylistDialog } from '../dialog/AddSongsToPlaylistDialog';
|
|
|
import { router } from '@kit.ArkUI';
|
|
import { router } from '@kit.ArkUI';
|
|
|
|
|
+import { GlobalContext } from '../common/util/GlobalContext';
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 简化的歌单播放事件数据
|
|
|
|
|
+ */
|
|
|
|
|
+interface SimplifiedPlaylistEventData {
|
|
|
|
|
+ playlistId: string;
|
|
|
|
|
+ playlistName: string;
|
|
|
|
|
+ songCount: number;
|
|
|
|
|
+ startIndex: number;
|
|
|
|
|
+ songFilePaths: string[];
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 歌单详情页面
|
|
* 歌单详情页面
|
|
@@ -17,8 +31,14 @@ export struct PlaylistDetailPage {
|
|
|
@State songList: VideoItem[] = []
|
|
@State songList: VideoItem[] = []
|
|
|
@State isLoading: boolean = true
|
|
@State isLoading: boolean = true
|
|
|
@State isShowEditDialog: boolean = false
|
|
@State isShowEditDialog: boolean = false
|
|
|
-
|
|
|
|
|
|
|
+ @State isPlaying: boolean = false
|
|
|
|
|
+ @State curIndex: number = -1
|
|
|
|
|
+ @State pageOpacity: number = 0
|
|
|
|
|
+ @State contentScale: number = 0.95
|
|
|
|
|
+ @State showContent: boolean = false
|
|
|
|
|
+
|
|
|
private playlistTable: PlaylistTable = new PlaylistTable(getContext(this))
|
|
private playlistTable: PlaylistTable = new PlaylistTable(getContext(this))
|
|
|
|
|
+ private mediaTable: MediaTable = new MediaTable(getContext(this))
|
|
|
private playlistId: string = ''
|
|
private playlistId: string = ''
|
|
|
|
|
|
|
|
aboutToAppear() {
|
|
aboutToAppear() {
|
|
@@ -29,6 +49,14 @@ export struct PlaylistDetailPage {
|
|
|
this.playlistId = this.playlist.id
|
|
this.playlistId = this.playlist.id
|
|
|
this.loadPlaylistDetail()
|
|
this.loadPlaylistDetail()
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // 监听播放状态变化
|
|
|
|
|
+ this.setupPlaybackStatusListener()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ aboutToDisappear() {
|
|
|
|
|
+ // 移除事件监听
|
|
|
|
|
+ this.removePlaybackStatusListener()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -50,10 +78,106 @@ export struct PlaylistDetailPage {
|
|
|
// 将 PlaylistSong 转换为 VideoItem
|
|
// 将 PlaylistSong 转换为 VideoItem
|
|
|
this.songList = await this.convertPlaylistSongsToVideoItems(playlistSongs)
|
|
this.songList = await this.convertPlaylistSongsToVideoItems(playlistSongs)
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- console.error('加载歌单详情失败:', error)
|
|
|
|
|
|
|
+ LogUtil.error('heanup 加载歌单详情失败: ' + error)
|
|
|
ToastUtil.showToast('加载歌单详情失败')
|
|
ToastUtil.showToast('加载歌单详情失败')
|
|
|
} finally {
|
|
} finally {
|
|
|
this.isLoading = false
|
|
this.isLoading = false
|
|
|
|
|
+ // 启动页面进入动画
|
|
|
|
|
+ this.animatePageEntry()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 格式化时长显示
|
|
|
|
|
+ */
|
|
|
|
|
+ formatDuration(duration?: number): string {
|
|
|
|
|
+ if (!duration || duration <= 0) {
|
|
|
|
|
+ return ''
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const minutes = Math.floor(duration / 60)
|
|
|
|
|
+ const seconds = Math.floor(duration % 60)
|
|
|
|
|
+ return `${minutes}:${seconds.toString().padStart(2, '0')}`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算歌单总时长
|
|
|
|
|
+ */
|
|
|
|
|
+ calculateTotalDuration(): number {
|
|
|
|
|
+ let total = 0
|
|
|
|
|
+ for (const song of this.songList) {
|
|
|
|
|
+ const duration = song.duration || 0
|
|
|
|
|
+ total = total + (typeof duration === 'number' ? duration : 0)
|
|
|
|
|
+ }
|
|
|
|
|
+ return total
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 格式化总时长显示
|
|
|
|
|
+ */
|
|
|
|
|
+ formatTotalDuration(totalSeconds: number): string {
|
|
|
|
|
+ if (totalSeconds <= 0) {
|
|
|
|
|
+ return ''
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const hours = Math.floor(totalSeconds / 3600)
|
|
|
|
|
+ const minutes = Math.floor((totalSeconds % 3600) / 60)
|
|
|
|
|
+
|
|
|
|
|
+ if (hours > 0) {
|
|
|
|
|
+ return `${hours}小时${minutes}分钟`
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return `${minutes}分钟`
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 页面入场动画
|
|
|
|
|
+ */
|
|
|
|
|
+ animatePageEntry() {
|
|
|
|
|
+ animateTo({
|
|
|
|
|
+ duration: 600,
|
|
|
|
|
+ curve: Curve.EaseOut,
|
|
|
|
|
+ delay: 100,
|
|
|
|
|
+ onFinish: () => {
|
|
|
|
|
+ this.showContent = true
|
|
|
|
|
+ }
|
|
|
|
|
+ }, () => {
|
|
|
|
|
+ this.pageOpacity = 1
|
|
|
|
|
+ this.contentScale = 1
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 设置播放状态监听
|
|
|
|
|
+ */
|
|
|
|
|
+ setupPlaybackStatusListener() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 监听播放状态变化事件
|
|
|
|
|
+ const eventPlaybackStatus: emitter.InnerEvent = { eventId: 2003 }
|
|
|
|
|
+ emitter.on(eventPlaybackStatus, (eventData: emitter.EventData) => {
|
|
|
|
|
+ LogUtil.info('heanup PlaylistDetailPage 收到播放状态变化事件')
|
|
|
|
|
+ if (eventData.data) {
|
|
|
|
|
+ const data = eventData.data as Record<string, Object>
|
|
|
|
|
+ this.isPlaying = data['isPlaying'] as boolean || false
|
|
|
|
|
+ this.curIndex = data['curIndex'] as number || -1
|
|
|
|
|
+
|
|
|
|
|
+ // 如果当前播放的是这个歌单的歌曲,高亮显示
|
|
|
|
|
+ LogUtil.info(`heanup 播放状态更新: isPlaying=${this.isPlaying}, curIndex=${this.curIndex}`)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ LogUtil.error('heanup 设置播放状态监听失败: ' + error)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 移除播放状态监听
|
|
|
|
|
+ */
|
|
|
|
|
+ removePlaybackStatusListener() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ emitter.off(2003)
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ LogUtil.error('heanup 移除播放状态监听失败: ' + error)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -62,36 +186,50 @@ export struct PlaylistDetailPage {
|
|
|
*/
|
|
*/
|
|
|
async convertPlaylistSongsToVideoItems(playlistSongs: PlaylistSong[]): Promise<VideoItem[]> {
|
|
async convertPlaylistSongsToVideoItems(playlistSongs: PlaylistSong[]): Promise<VideoItem[]> {
|
|
|
const videoItems: VideoItem[] = []
|
|
const videoItems: VideoItem[] = []
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ LogUtil.info(`heanup 开始转换歌曲,共 ${playlistSongs.length} 首`)
|
|
|
|
|
+
|
|
|
for (const playlistSong of playlistSongs) {
|
|
for (const playlistSong of playlistSongs) {
|
|
|
try {
|
|
try {
|
|
|
- // 获取文件名
|
|
|
|
|
- const fileName = playlistSong.songFilePath.split('/').pop() || ''
|
|
|
|
|
- const name = fileName.replace(/\.[^/.]+$/, "") // 移除文件扩展名
|
|
|
|
|
-
|
|
|
|
|
- // 创建基本的 VideoItem 对象
|
|
|
|
|
- const videoItem = new VideoItem(
|
|
|
|
|
- name, // name
|
|
|
|
|
- Date.now().toString(), // id
|
|
|
|
|
- playlistSong.songFilePath, // filePath
|
|
|
|
|
- 0, // type (音乐类型)
|
|
|
|
|
- 0, // videoSize
|
|
|
|
|
- new Date().toISOString(), // cTime
|
|
|
|
|
- undefined, // pixelMap
|
|
|
|
|
- undefined, // size
|
|
|
|
|
- undefined, // pixelMapPath
|
|
|
|
|
- undefined, // artist
|
|
|
|
|
- undefined, // album
|
|
|
|
|
- fileName, // fileName
|
|
|
|
|
- undefined // lastPlayed
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- videoItems.push(videoItem)
|
|
|
|
|
|
|
+ LogUtil.info(`heanup 查询歌曲: ${playlistSong.songFilePath}`)
|
|
|
|
|
+
|
|
|
|
|
+ // 从数据库查询完整的歌曲信息
|
|
|
|
|
+ const videoItem = await this.mediaTable.queryVideoByFilePath(playlistSong.songFilePath)
|
|
|
|
|
+
|
|
|
|
|
+ if (videoItem) {
|
|
|
|
|
+ LogUtil.info(`heanup 找到歌曲: ${videoItem.name}`)
|
|
|
|
|
+ videoItems.push(videoItem)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ LogUtil.warn(`heanup 数据库中未找到歌曲: ${playlistSong.songFilePath}`)
|
|
|
|
|
+
|
|
|
|
|
+ // 如果数据库中没有,创建一个基本的 VideoItem
|
|
|
|
|
+ const fileName = playlistSong.songFilePath.split('/').pop() || ''
|
|
|
|
|
+ const name = fileName.replace(/\.[^/.]+$/, "") // 移除文件扩展名
|
|
|
|
|
+
|
|
|
|
|
+ const basicVideoItem = new VideoItem(
|
|
|
|
|
+ name, // name
|
|
|
|
|
+ Date.now().toString() + Math.random(), // id
|
|
|
|
|
+ playlistSong.songFilePath, // filePath
|
|
|
|
|
+ 0, // type (音乐类型)
|
|
|
|
|
+ 0, // videoSize
|
|
|
|
|
+ playlistSong.addTime, // cTime
|
|
|
|
|
+ undefined, // pixelMap
|
|
|
|
|
+ undefined, // size
|
|
|
|
|
+ undefined, // pixelMapPath
|
|
|
|
|
+ undefined, // artist
|
|
|
|
|
+ undefined, // album
|
|
|
|
|
+ fileName, // fileName
|
|
|
|
|
+ undefined // lastPlayed
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ videoItems.push(basicVideoItem)
|
|
|
|
|
+ }
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- console.error('转换歌曲失败:', error)
|
|
|
|
|
|
|
+ LogUtil.error('heanup 转换歌曲失败: ' + error)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ LogUtil.info(`heanup 转换完成,共 ${videoItems.length} 首歌曲`)
|
|
|
return videoItems
|
|
return videoItems
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -106,32 +244,64 @@ export struct PlaylistDetailPage {
|
|
|
|
|
|
|
|
// 发送播放歌单事件
|
|
// 发送播放歌单事件
|
|
|
const eventPlaylistPlay: emitter.InnerEvent = { eventId: 2002 }
|
|
const eventPlaylistPlay: emitter.InnerEvent = { eventId: 2002 }
|
|
|
|
|
+
|
|
|
|
|
+ LogUtil.info(`heanup 准备发送播放事件,playlist: ${this.playlist ? '存在' : '不存在'}, songs: ${this.songList.length}首`)
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试简化数据结构,只发送必要的信息
|
|
|
|
|
+ const simplifiedData: SimplifiedPlaylistEventData = {
|
|
|
|
|
+ playlistId: this.playlist?.id || '',
|
|
|
|
|
+ playlistName: this.playlist?.name || '',
|
|
|
|
|
+ songCount: this.songList.length,
|
|
|
|
|
+ startIndex: 0,
|
|
|
|
|
+ // 只发送歌曲的必要信息
|
|
|
|
|
+ songFilePaths: this.songList.map(song => song.filePath)
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ LogUtil.info(`heanup 发送简化事件数据: ${JSON.stringify(simplifiedData)}`)
|
|
|
|
|
+
|
|
|
const eventData: emitter.EventData = {
|
|
const eventData: emitter.EventData = {
|
|
|
- data: {
|
|
|
|
|
- playlist: this.playlist,
|
|
|
|
|
- songs: this.songList,
|
|
|
|
|
- startIndex: 0
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ data: simplifiedData
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
emitter.emit(eventPlaylistPlay, eventData)
|
|
emitter.emit(eventPlaylistPlay, eventData)
|
|
|
|
|
|
|
|
ToastUtil.showToast('开始播放歌单')
|
|
ToastUtil.showToast('开始播放歌单')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 播放指定歌曲
|
|
|
|
|
|
|
+ * 撌放指定歌曲
|
|
|
*/
|
|
*/
|
|
|
playSong(song: VideoItem, index: number) {
|
|
playSong(song: VideoItem, index: number) {
|
|
|
- // 发送播放歌单事件,指定开始播放的歌曲
|
|
|
|
|
|
|
+ LogUtil.info(`heanup === playSong 方法被调用 ===`)
|
|
|
|
|
+ LogUtil.info(`heanup 播放指定歌曲: ${song.name}, 索引: ${index}`)
|
|
|
|
|
+ LogUtil.info(`heanup 歌曲列表长度: ${this.songList.length}`)
|
|
|
|
|
+ LogUtil.info(`heanup 歌单ID: ${this.playlist?.id}, 歌单名称: ${this.playlist?.name}`)
|
|
|
|
|
+
|
|
|
|
|
+ // 检查歌曲文件路径
|
|
|
|
|
+ LogUtil.info(`heanup 所有歌曲文件路径: ${JSON.stringify(this.songList.map(s => s.filePath))}`)
|
|
|
|
|
+
|
|
|
|
|
+ // 发送播放歌单事件,使用简化数据结构
|
|
|
const eventPlaylistPlay: emitter.InnerEvent = { eventId: 2002 }
|
|
const eventPlaylistPlay: emitter.InnerEvent = { eventId: 2002 }
|
|
|
|
|
+ const simplifiedData: SimplifiedPlaylistEventData = {
|
|
|
|
|
+ playlistId: this.playlist?.id || '',
|
|
|
|
|
+ playlistName: this.playlist?.name || '',
|
|
|
|
|
+ songCount: this.songList.length,
|
|
|
|
|
+ startIndex: index,
|
|
|
|
|
+ // 只发送所有歌曲的文件路径
|
|
|
|
|
+ songFilePaths: this.songList.map(s => s.filePath)
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ LogUtil.info(`heanup 准备发送事件,eventId: ${eventPlaylistPlay.eventId}`)
|
|
|
|
|
+ LogUtil.info(`heanup 发送单歌曲播放事件数据: ${JSON.stringify(simplifiedData)}`)
|
|
|
|
|
+
|
|
|
const eventData: emitter.EventData = {
|
|
const eventData: emitter.EventData = {
|
|
|
- data: {
|
|
|
|
|
- playlist: this.playlist,
|
|
|
|
|
- songs: this.songList,
|
|
|
|
|
- startIndex: index
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ data: simplifiedData
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+ LogUtil.info(`heanup eventData对象: ${JSON.stringify(eventData)}`)
|
|
|
|
|
+ LogUtil.info(`heanup 即将调用 emitter.emit`)
|
|
|
emitter.emit(eventPlaylistPlay, eventData)
|
|
emitter.emit(eventPlaylistPlay, eventData)
|
|
|
|
|
+ LogUtil.info(`heanup emitter.emit 调用完成`)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -274,18 +444,31 @@ export struct PlaylistDetailPage {
|
|
|
.layoutWeight(1)
|
|
.layoutWeight(1)
|
|
|
.justifyContent(FlexAlign.Center)
|
|
.justifyContent(FlexAlign.Center)
|
|
|
} else if (this.playlist) {
|
|
} else if (this.playlist) {
|
|
|
- // 歌单信息区域
|
|
|
|
|
|
|
+ // 歌单信息区域 - 参考LocalMusic的封面标题区域设计
|
|
|
Column() {
|
|
Column() {
|
|
|
- // 歌单封面和基本信息
|
|
|
|
|
|
|
+ // 背景区域
|
|
|
Row() {
|
|
Row() {
|
|
|
- Image(this.playlist.coverPath || $r('app.media.hm_playlist'))
|
|
|
|
|
- .width(120)
|
|
|
|
|
- .height(120)
|
|
|
|
|
- .borderRadius(8)
|
|
|
|
|
- .clip(true)
|
|
|
|
|
- .margin({ right: 16 })
|
|
|
|
|
-
|
|
|
|
|
Column() {
|
|
Column() {
|
|
|
|
|
+ // 歌单封面
|
|
|
|
|
+ Image(this.playlist.coverPath || $r('app.media.hm_playlist'))
|
|
|
|
|
+ .width(100)
|
|
|
|
|
+ .height(100)
|
|
|
|
|
+ .borderRadius(12)
|
|
|
|
|
+ .clip(true)
|
|
|
|
|
+ .interpolation(ImageInterpolation.High)
|
|
|
|
|
+ .autoResize(true)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: 15,
|
|
|
|
|
+ color: '#0000001a',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 6
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ .alignItems(HorizontalAlign.Start)
|
|
|
|
|
+ .margin({ left: 24, top: 16, bottom: 16 })
|
|
|
|
|
+
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ // 歌单名称
|
|
|
Text(this.playlist.name)
|
|
Text(this.playlist.name)
|
|
|
.fontSize(20)
|
|
.fontSize(20)
|
|
|
.fontWeight(FontWeight.Bold)
|
|
.fontWeight(FontWeight.Bold)
|
|
@@ -293,119 +476,498 @@ export struct PlaylistDetailPage {
|
|
|
.maxLines(2)
|
|
.maxLines(2)
|
|
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
.margin({ bottom: 8 })
|
|
.margin({ bottom: 8 })
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 歌单描述
|
|
|
if (this.playlist.description) {
|
|
if (this.playlist.description) {
|
|
|
Text(this.playlist.description)
|
|
Text(this.playlist.description)
|
|
|
.fontSize(14)
|
|
.fontSize(14)
|
|
|
.fontColor($r('app.color.text_color'))
|
|
.fontColor($r('app.color.text_color'))
|
|
|
- .opacity(0.7)
|
|
|
|
|
- .maxLines(3)
|
|
|
|
|
|
|
+ .opacity(0.8)
|
|
|
|
|
+ .maxLines(2)
|
|
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
- .margin({ bottom: 8 })
|
|
|
|
|
|
|
+ .margin({ bottom: 12 })
|
|
|
|
|
+ .lineHeight(18)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- Text(`${this.playlist.songCount}首歌曲`)
|
|
|
|
|
- .fontSize(12)
|
|
|
|
|
- .fontColor($r('app.color.text_color'))
|
|
|
|
|
- .opacity(0.6)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 歌单统计信息
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ Text(`共${this.playlist.songCount}首歌`)
|
|
|
|
|
+ .fontSize(13)
|
|
|
|
|
+ .fontWeight(FontWeight.Bold)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .opacity(0.8)
|
|
|
|
|
+
|
|
|
|
|
+ if (this.calculateTotalDuration() > 0) {
|
|
|
|
|
+ Text(` · ${this.formatTotalDuration(this.calculateTotalDuration())}`)
|
|
|
|
|
+ .fontSize(13)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .opacity(0.7)
|
|
|
|
|
+ .margin({ left: 4 })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .margin({ top: 8 })
|
|
|
}
|
|
}
|
|
|
|
|
+ .height('100%')
|
|
|
.layoutWeight(1)
|
|
.layoutWeight(1)
|
|
|
|
|
+ .margin({ left: 16 })
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
.alignItems(HorizontalAlign.Start)
|
|
.alignItems(HorizontalAlign.Start)
|
|
|
}
|
|
}
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
- .padding({ left: 16, right: 16, top: 16, bottom: 16 })
|
|
|
|
|
-
|
|
|
|
|
- // 操作按钮
|
|
|
|
|
- Row({ space: 12 }) {
|
|
|
|
|
- Button('播放全部')
|
|
|
|
|
- .width('45%')
|
|
|
|
|
- .height(40)
|
|
|
|
|
- .backgroundColor($r('app.color.theme_color'))
|
|
|
|
|
- .borderRadius(20)
|
|
|
|
|
- .fontSize(14)
|
|
|
|
|
- .fontColor(Color.White)
|
|
|
|
|
- .onClick(() => {
|
|
|
|
|
- this.playPlaylist()
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- Button('编辑歌单')
|
|
|
|
|
- .width('45%')
|
|
|
|
|
- .height(40)
|
|
|
|
|
- .backgroundColor($r('app.color.secondary_button_background'))
|
|
|
|
|
- .borderRadius(20)
|
|
|
|
|
- .fontSize(14)
|
|
|
|
|
- .fontColor($r('app.color.text_color'))
|
|
|
|
|
- .onClick(() => {
|
|
|
|
|
- this.editPlaylist()
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ .height(140)
|
|
|
|
|
+ .justifyContent(FlexAlign.SpaceBetween)
|
|
|
|
|
+ .padding({ right: 24 })
|
|
|
|
|
+
|
|
|
|
|
+ // 操作按钮区域
|
|
|
|
|
+ Row({ space: 16 }) {
|
|
|
|
|
+ // 播放全部按钮 - 参考LocalMusic的按钮样式
|
|
|
|
|
+ Button() {
|
|
|
|
|
+ Row({ space: 8 }) {
|
|
|
|
|
+ Image($r('app.media.ic_play'))
|
|
|
|
|
+ .width(16)
|
|
|
|
|
+ .height(16)
|
|
|
|
|
+ .fillColor(Color.White)
|
|
|
|
|
+
|
|
|
|
|
+ Text('播放全部')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor(Color.White)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ }
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width(140)
|
|
|
|
|
+ .height(44)
|
|
|
|
|
+ .backgroundColor($r('app.color.theme_color'))
|
|
|
|
|
+ .borderRadius(22)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: 8,
|
|
|
|
|
+ color: '#0a59f740',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 4
|
|
|
|
|
+ })
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.playPlaylist()
|
|
|
|
|
+ })
|
|
|
|
|
+ .stateStyles({
|
|
|
|
|
+ normal: {
|
|
|
|
|
+ .backgroundColor($r('app.color.theme_color'))
|
|
|
|
|
+ .scale({ x: 1, y: 1 })
|
|
|
|
|
+ },
|
|
|
|
|
+ pressed: {
|
|
|
|
|
+ .backgroundColor('#0a59f7cc')
|
|
|
|
|
+ .scale({ x: 0.96, y: 0.96 })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .animation({
|
|
|
|
|
+ duration: 150,
|
|
|
|
|
+ curve: Curve.EaseInOut
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 编辑歌单按钮
|
|
|
|
|
+ Button() {
|
|
|
|
|
+ Row({ space: 6 }) {
|
|
|
|
|
+ Text('✏️')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontColor($r('app.color.theme_color'))
|
|
|
|
|
+
|
|
|
|
|
+ Text('编辑')
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor($r('app.color.theme_color'))
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ }
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width(100)
|
|
|
|
|
+ .height(44)
|
|
|
|
|
+ .backgroundColor(Color.Transparent)
|
|
|
|
|
+ .borderRadius(22)
|
|
|
|
|
+ .border({
|
|
|
|
|
+ width: 1.5,
|
|
|
|
|
+ color: $r('app.color.theme_color')
|
|
|
|
|
+ })
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.editPlaylist()
|
|
|
|
|
+ })
|
|
|
|
|
+ .stateStyles({
|
|
|
|
|
+ normal: {
|
|
|
|
|
+ .backgroundColor(Color.Transparent)
|
|
|
|
|
+ .scale({ x: 1, y: 1 })
|
|
|
|
|
+ },
|
|
|
|
|
+ pressed: {
|
|
|
|
|
+ .backgroundColor('#0a59f715')
|
|
|
|
|
+ .scale({ x: 0.96, y: 0.96 })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .animation({
|
|
|
|
|
+ duration: 150,
|
|
|
|
|
+ curve: Curve.EaseInOut
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 删除歌单按钮
|
|
|
|
|
+ Button() {
|
|
|
|
|
+ Text('🗑️')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontColor('#ff4757')
|
|
|
|
|
+ }
|
|
|
|
|
+ .width(44)
|
|
|
|
|
+ .height(44)
|
|
|
|
|
+ .backgroundColor(Color.Transparent)
|
|
|
|
|
+ .borderRadius(22)
|
|
|
|
|
+ .border({
|
|
|
|
|
+ width: 1.5,
|
|
|
|
|
+ color: '#ff4757'
|
|
|
|
|
+ })
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.deletePlaylist()
|
|
|
|
|
+ })
|
|
|
|
|
+ .stateStyles({
|
|
|
|
|
+ normal: {
|
|
|
|
|
+ .backgroundColor(Color.Transparent)
|
|
|
|
|
+ .scale({ x: 1, y: 1 })
|
|
|
|
|
+ },
|
|
|
|
|
+ pressed: {
|
|
|
|
|
+ .backgroundColor('#ff475715')
|
|
|
|
|
+ .scale({ x: 0.96, y: 0.96 })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .animation({
|
|
|
|
|
+ duration: 150,
|
|
|
|
|
+ curve: Curve.EaseInOut
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
- .padding({ left: 16, right: 16, bottom: 16 })
|
|
|
|
|
|
|
+ .padding({ left: 24, right: 24, bottom: 20 })
|
|
|
|
|
+ .justifyContent(FlexAlign.Start)
|
|
|
}
|
|
}
|
|
|
.backgroundColor($r('app.color.bg_card'))
|
|
.backgroundColor($r('app.color.bg_card'))
|
|
|
.margin({ left: 16, right: 16, top: 16 })
|
|
.margin({ left: 16, right: 16, top: 16 })
|
|
|
- .borderRadius(12)
|
|
|
|
|
|
|
+ .borderRadius(16)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: 12,
|
|
|
|
|
+ color: '#00000014',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 4
|
|
|
|
|
+ })
|
|
|
|
|
+ .scale({ x: this.contentScale, y: this.contentScale })
|
|
|
|
|
+ .opacity(this.pageOpacity)
|
|
|
|
|
+ .transition(TransitionEffect.OPACITY.animation({ duration: 600, curve: Curve.EaseOut }))
|
|
|
|
|
+ .transition(TransitionEffect.scale({ x: 0.95, y: 0.95 }).animation({ duration: 600, curve: Curve.EaseOut }))
|
|
|
|
|
+
|
|
|
|
|
+ // 歌曲列表标题 - 简化设计,与LocalMusic保持一致
|
|
|
|
|
+ if (this.songList.length > 0) {
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ Text('歌曲列表')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .opacity(0.9)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+
|
|
|
|
|
+ Text(`${this.songList.length}首`)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .opacity(0.6)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding({ left: 24, right: 24, top: 16, bottom: 12 })
|
|
|
|
|
+ .opacity(this.pageOpacity)
|
|
|
|
|
+ .transition(TransitionEffect.OPACITY.animation({ duration: 800, curve: Curve.EaseOut, delay: 200 }))
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 歌曲列表
|
|
|
|
|
|
|
+ // 歌曲列表 - 参考LocalMusic的MusicItem样式
|
|
|
if (this.songList.length > 0) {
|
|
if (this.songList.length > 0) {
|
|
|
- List() {
|
|
|
|
|
|
|
+ List({ space: 0 }) {
|
|
|
ForEach(this.songList, (song: VideoItem, index: number) => {
|
|
ForEach(this.songList, (song: VideoItem, index: number) => {
|
|
|
ListItem() {
|
|
ListItem() {
|
|
|
- Row() {
|
|
|
|
|
- Text(`${index + 1}`)
|
|
|
|
|
- .fontSize(14)
|
|
|
|
|
- .fontColor($r('app.color.text_color'))
|
|
|
|
|
- .width(40)
|
|
|
|
|
- .textAlign(TextAlign.Center)
|
|
|
|
|
-
|
|
|
|
|
- Column() {
|
|
|
|
|
- Text(song.name || song.fileName || '')
|
|
|
|
|
- .fontSize(16)
|
|
|
|
|
- .fontColor($r('app.color.text_color'))
|
|
|
|
|
- .maxLines(1)
|
|
|
|
|
- .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
|
|
+ Button({ type: ButtonType.Normal, stateEffect: true }) {
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ // 音乐图标 - 圆形设计,参考LocalMusic
|
|
|
|
|
+ Stack() {
|
|
|
|
|
+ Image($r('app.media.music_red'))
|
|
|
|
|
+ .width(40)
|
|
|
|
|
+ .height(40)
|
|
|
|
|
+ .fillColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
|
|
|
|
|
+ .borderRadius('100%')
|
|
|
|
|
+ .clip(true)
|
|
|
|
|
+ .interpolation(ImageInterpolation.High)
|
|
|
|
|
+ .autoResize(true)
|
|
|
|
|
+ .margin({ left: 20 })
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.playSong(song, index)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 播放中动画覆盖层
|
|
|
|
|
+ if (this.isPlaying && this.curIndex === index) {
|
|
|
|
|
+ Text('♪')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontColor(Color.White)
|
|
|
|
|
+ .position({ x: 0, y: 0 })
|
|
|
|
|
+ .width(40)
|
|
|
|
|
+ .height(40)
|
|
|
|
|
+ .textAlign(TextAlign.Center)
|
|
|
|
|
+ .animation({
|
|
|
|
|
+ duration: 800,
|
|
|
|
|
+ curve: Curve.EaseInOut,
|
|
|
|
|
+ iterations: -1,
|
|
|
|
|
+ playMode: PlayMode.Alternate
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .width(60)
|
|
|
|
|
+
|
|
|
|
|
+ // 歌曲信息 - 参考LocalMusic的布局
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ // 歌曲名称
|
|
|
|
|
+ Text(song.name || song.fileName || '')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
|
|
|
|
|
+ .fontWeight(this.isPlaying && this.curIndex === index ? FontWeight.Medium : FontWeight.Normal)
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .margin({ top: 8, left: 12 })
|
|
|
|
|
+
|
|
|
|
|
+ // 歌手和专辑信息 - 参考LocalMusic的第二行布局
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ if (song.artist) {
|
|
|
|
|
+ Text(song.artist)
|
|
|
|
|
+ .fontSize(13)
|
|
|
|
|
+ .fontColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .opacity(0.8)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (song.artist && song.album) {
|
|
|
|
|
+ Text('·')
|
|
|
|
|
+ .fontSize(13)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .opacity(0.6)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (song.album) {
|
|
|
|
|
+ Text(song.album)
|
|
|
|
|
+ .fontSize(13)
|
|
|
|
|
+ .fontColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .opacity(0.8)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Blank()
|
|
|
|
|
+
|
|
|
|
|
+ // 时长显示
|
|
|
|
|
+ if (song.duration && typeof song.duration === 'number' && song.duration > 0) {
|
|
|
|
|
+ Text(this.formatDuration(song.duration))
|
|
|
|
|
+ .fontSize(13)
|
|
|
|
|
+ .fontColor(this.isPlaying && this.curIndex === index ? $r('app.color.theme_color') : $r('app.color.text_color'))
|
|
|
|
|
+ .opacity(0.7)
|
|
|
|
|
+ .margin({ right: 20 })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
|
|
+ .margin({ left: 12, top: 4 })
|
|
|
|
|
+ .alignItems(VerticalAlign.Center)
|
|
|
|
|
+ }
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .alignItems(HorizontalAlign.Start)
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
|
|
+
|
|
|
|
|
+ // 更多操作按钮
|
|
|
|
|
+ Text('⋯')
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .opacity(0.6)
|
|
|
|
|
+ .margin({ right: 15 })
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.showSongMenu(song)
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
- .layoutWeight(1)
|
|
|
|
|
- .alignItems(HorizontalAlign.Start)
|
|
|
|
|
- .margin({ left: 16 })
|
|
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height(70)
|
|
|
|
|
+ .justifyContent(FlexAlign.Start)
|
|
|
|
|
+ .alignItems(VerticalAlign.Center)
|
|
|
}
|
|
}
|
|
|
|
|
+ .backgroundColor(Color.Transparent)
|
|
|
|
|
+ .height(70)
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
- .height(56)
|
|
|
|
|
- .padding({ left: 16, right: 16 })
|
|
|
|
|
.onClick(() => {
|
|
.onClick(() => {
|
|
|
this.playSong(song, index)
|
|
this.playSong(song, index)
|
|
|
})
|
|
})
|
|
|
- .gesture(LongPressGesture().onAction(() => {
|
|
|
|
|
- this.showSongMenu(song)
|
|
|
|
|
|
|
+ .gesture(
|
|
|
|
|
+ LongPressGesture()
|
|
|
|
|
+ .onAction(() => {
|
|
|
|
|
+ this.showSongMenu(song)
|
|
|
|
|
+ })
|
|
|
|
|
+ )
|
|
|
|
|
+ .stateStyles({
|
|
|
|
|
+ normal: {
|
|
|
|
|
+ .backgroundColor(Color.Transparent)
|
|
|
|
|
+ },
|
|
|
|
|
+ pressed: {
|
|
|
|
|
+ .backgroundColor('#f1f3f5')
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .opacity(this.pageOpacity)
|
|
|
|
|
+ .translate({ x: 0, y: this.showContent ? 0 : 20 })
|
|
|
|
|
+ .transition(TransitionEffect.OPACITY.animation({
|
|
|
|
|
+ duration: 600,
|
|
|
|
|
+ curve: Curve.EaseOut,
|
|
|
|
|
+ delay: 300 + index * 30
|
|
|
|
|
+ }))
|
|
|
|
|
+ .transition(TransitionEffect.translate({ y: 20 }).animation({
|
|
|
|
|
+ duration: 600,
|
|
|
|
|
+ curve: Curve.EaseOut,
|
|
|
|
|
+ delay: 300 + index * 30
|
|
|
}))
|
|
}))
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+ .width('100%')
|
|
|
.layoutWeight(1)
|
|
.layoutWeight(1)
|
|
|
- .padding({ left: 16, right: 16 })
|
|
|
|
|
|
|
+ .backgroundColor($r('app.color.bg_card'))
|
|
|
|
|
+ .margin({ left: 16, right: 16 })
|
|
|
|
|
+ .borderRadius(12)
|
|
|
|
|
+ .divider({
|
|
|
|
|
+ strokeWidth: 0.5,
|
|
|
|
|
+ color: '#0000001a',
|
|
|
|
|
+ startMargin: 80,
|
|
|
|
|
+ endMargin: 20
|
|
|
|
|
+ })
|
|
|
|
|
+ .opacity(this.pageOpacity)
|
|
|
|
|
+ .scale({ x: this.contentScale, y: this.contentScale })
|
|
|
|
|
+ .transition(TransitionEffect.OPACITY.animation({ duration: 800, curve: Curve.EaseOut, delay: 200 }))
|
|
|
|
|
+ .transition(TransitionEffect.scale({ x: 0.95, y: 0.95 }).animation({ duration: 800, curve: Curve.EaseOut, delay: 200 }))
|
|
|
} else {
|
|
} else {
|
|
|
// 空状态
|
|
// 空状态
|
|
|
Column() {
|
|
Column() {
|
|
|
- Image($r('app.media.music_red'))
|
|
|
|
|
- .width(80)
|
|
|
|
|
- .height(80)
|
|
|
|
|
- .opacity(0.3)
|
|
|
|
|
- .margin({ bottom: 16 })
|
|
|
|
|
-
|
|
|
|
|
- Text('歌单为空')
|
|
|
|
|
- .fontSize(16)
|
|
|
|
|
- .fontColor($r('app.color.text_color'))
|
|
|
|
|
- .opacity(0.6)
|
|
|
|
|
- .margin({ bottom: 8 })
|
|
|
|
|
-
|
|
|
|
|
- Text('添加一些歌曲到歌单中')
|
|
|
|
|
- .fontSize(14)
|
|
|
|
|
- .fontColor($r('app.color.text_color'))
|
|
|
|
|
- .opacity(0.4)
|
|
|
|
|
|
|
+ // 空状态容器
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ // 空状态图标容器
|
|
|
|
|
+ Stack() {
|
|
|
|
|
+ // 背景圆圈
|
|
|
|
|
+ Circle({ width: 160, height: 160 })
|
|
|
|
|
+ .fill('#0a59f715')
|
|
|
|
|
+ .border({
|
|
|
|
|
+ width: 2,
|
|
|
|
|
+ color: '#0a59f71a'
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 中间圆圈
|
|
|
|
|
+ Circle({ width: 120, height: 120 })
|
|
|
|
|
+ .fill('#0a59f722')
|
|
|
|
|
+
|
|
|
|
|
+ // 音符图标
|
|
|
|
|
+ Image($r('app.media.music_red'))
|
|
|
|
|
+ .width(64)
|
|
|
|
|
+ .height(64)
|
|
|
|
|
+ .opacity(0.6)
|
|
|
|
|
+ .fillColor($r('app.color.theme_color'))
|
|
|
|
|
+ }
|
|
|
|
|
+ .margin({ bottom: 32 })
|
|
|
|
|
+
|
|
|
|
|
+ // 空状态标题
|
|
|
|
|
+ Text('歌单还是空的')
|
|
|
|
|
+ .fontSize(22)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .fontWeight(FontWeight.Bold)
|
|
|
|
|
+ .margin({ bottom: 12 })
|
|
|
|
|
+ .letterSpacing(0.5)
|
|
|
|
|
+
|
|
|
|
|
+ // 空状态描述
|
|
|
|
|
+ Text('快来添加你喜欢的音乐吧\n让这个歌单充满美妙的旋律')
|
|
|
|
|
+ .fontSize(15)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .opacity(0.8)
|
|
|
|
|
+ .margin({ bottom: 40 })
|
|
|
|
|
+ .textAlign(TextAlign.Center)
|
|
|
|
|
+ .lineHeight(24)
|
|
|
|
|
+ .maxLines(2)
|
|
|
|
|
+
|
|
|
|
|
+ // 添加歌曲按钮
|
|
|
|
|
+ Button() {
|
|
|
|
|
+ Row({ space: 8 }) {
|
|
|
|
|
+ Text('+')
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+ .fontColor(Color.White)
|
|
|
|
|
+
|
|
|
|
|
+ Text('添加歌曲')
|
|
|
|
|
+ .fontSize(16)
|
|
|
|
|
+ .fontColor(Color.White)
|
|
|
|
|
+ .fontWeight(FontWeight.Medium)
|
|
|
|
|
+ }
|
|
|
|
|
+ .justifyContent(FlexAlign.Center)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width(160)
|
|
|
|
|
+ .height(48)
|
|
|
|
|
+ .backgroundColor($r('app.color.theme_color'))
|
|
|
|
|
+ .borderRadius(24)
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: 12,
|
|
|
|
|
+ color: '#0a59f74d',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 6
|
|
|
|
|
+ })
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ showAddSongsToPlaylistDialog(
|
|
|
|
|
+ this.playlist!,
|
|
|
|
|
+ async (songs: VideoItem[]) => {
|
|
|
|
|
+ // 添加选中的歌曲到歌单
|
|
|
|
|
+ const success = await this.playlistTable.addSongsToPlaylist(this.playlistId, songs.map(song => song.filePath))
|
|
|
|
|
+
|
|
|
|
|
+ if (success) {
|
|
|
|
|
+ ToastUtil.showToast(`成功添加 ${songs.length} 首歌曲`)
|
|
|
|
|
+ // 重新加载歌单详情
|
|
|
|
|
+ this.loadPlaylistDetail()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ToastUtil.showToast('添加歌曲失败')
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
|
|
+ .stateStyles({
|
|
|
|
|
+ normal: {
|
|
|
|
|
+ .backgroundColor($r('app.color.theme_color'))
|
|
|
|
|
+ .scale({ x: 1, y: 1 })
|
|
|
|
|
+ },
|
|
|
|
|
+ pressed: {
|
|
|
|
|
+ .backgroundColor('#0a59f7cc')
|
|
|
|
|
+ .scale({ x: 0.96, y: 0.96 })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .animation({
|
|
|
|
|
+ duration: 200,
|
|
|
|
|
+ curve: Curve.EaseInOut
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 快速操作提示
|
|
|
|
|
+ Text('或长按歌单选择更多操作')
|
|
|
|
|
+ .fontSize(13)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .margin({ top: 16 })
|
|
|
|
|
+ .opacity(0.7)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .padding(32)
|
|
|
|
|
+ .backgroundColor($r('app.color.bg_card'))
|
|
|
|
|
+ .borderRadius(20)
|
|
|
|
|
+ .margin({ left: 16, right: 16 })
|
|
|
|
|
+ .shadow({
|
|
|
|
|
+ radius: 16,
|
|
|
|
|
+ color: '#0000000f',
|
|
|
|
|
+ offsetX: 0,
|
|
|
|
|
+ offsetY: 8
|
|
|
|
|
+ })
|
|
|
|
|
+ .opacity(this.pageOpacity)
|
|
|
|
|
+ .scale({ x: this.contentScale, y: this.contentScale })
|
|
|
|
|
+ .transition(TransitionEffect.OPACITY.animation({ duration: 800, curve: Curve.EaseOut, delay: 200 }))
|
|
|
|
|
+ .transition(TransitionEffect.scale({ x: 0.95, y: 0.95 }).animation({ duration: 800, curve: Curve.EaseOut, delay: 200 }))
|
|
|
}
|
|
}
|
|
|
.layoutWeight(1)
|
|
.layoutWeight(1)
|
|
|
.justifyContent(FlexAlign.Center)
|
|
.justifyContent(FlexAlign.Center)
|
|
|
|
|
+ .padding({ top: 20, bottom: 20 })
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|