| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- import { DialogHelper } from '@pura/harmony-dialog';
- import { Playlist } from '../viewmodel/Playlist';
- import { ToastUtil, LogUtil, StrUtil } from '@pura/harmony-utils';
- import { VideoItem } from '../viewmodel/VideoItem';
- import MediaTable from '../common/util/MediaTable';
- import PlaylistTable from '../common/util/PlaylistTable';
- import { LazyDataSource } from '../common/util/LazyDataSource';
- import { ConfigurationConstant, common } from '@kit.AbilityKit';
- import { CommonConstants } from '../common/constants/CommonConstants';
- // 工具函数:将 #RRGGBB 字符串和透明度转为 'rgba(r,g,b,a)' 字符串,深色模式下可返回深色变体
- function themeColorWithAlpha(themeColor: string, alpha: number, isDarkMode: boolean = false): string {
- if (isDarkMode) {
- // 深色模式下返回更深的灰色或半透明黑色
- return `rgba(34,34,34,${alpha + 0.2 > 1 ? 1 : alpha + 0.2})`;
- }
- const color = themeColor.replace('#', '');
- const r = parseInt(color.substring(0, 2), 16);
- const g = parseInt(color.substring(2, 4), 16);
- const b = parseInt(color.substring(4, 6), 16);
- return `rgba(${r},${g},${b},${alpha})`;
- }
- /**
- * 添加歌曲到歌单对话框内容组件
- */
- @Component
- struct AddSongsToPlaylistDialogContent {
- context = this.getUIContext().getHostContext() as common.UIAbilityContext
- @State opacityItem: number = 1; // 控制透明度的状态变量
- @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
- @State isDarkMode: boolean = false
- @StorageProp('currentColorMode') @Watch('onColorModeChange') currentMode: number =
- ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
- onColorModeChange() {
- this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
- }
- @State selectedSongs: VideoItem[] = []
- @State isLoading: boolean = false
- @State searchText: string = ''
- @State filteredSongs: VideoItem[] = []
- private listScroller: ListScroller = new ListScroller()
- @State isSearchMode: boolean = false
- @Prop playlist: Playlist
- @StorageProp('mediaKuList') mediaKuList: Array<VideoItem> = []; //媒体库文件
- private mediaTable: MediaTable = new MediaTable(getContext(this))
- private playlistTable: PlaylistTable = new PlaylistTable(getContext(this))
- @State dataSource: LazyDataSource<VideoItem> = new LazyDataSource([])
- // 分页相关状态
- @State currentPage: number = 0
- @State hasMoreData: boolean = true
- private readonly PAGE_SIZE: number = 50
- // 用于存储完整数据的引用
- private allSongs: VideoItem[] = []
- // 回调函数
- onConfirm?: (songs: VideoItem[]) => void
- onCancel?: () => void
- aboutToAppear() {
- // 初始化深色模式状态
- this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
- LogUtil.info('heanup AddSongsToPlaylistDialogContent aboutToAppear 开始')
- LogUtil.info('heanup 初始化深色模式状态: ' + this.isDarkMode + ', currentMode: ' + this.currentMode)
- LogUtil.info('heanup playlist: ' +
- (this.playlist ? JSON.stringify({ id: this.playlist.id, name: this.playlist.name }) : 'null'))
- console.info('onecold mediaKuList 对话框 aboutToAppear=' + this.mediaKuList.length)
- this.allSongs = [...this.mediaKuList]
- this.filteredSongs = [...this.mediaKuList]
- this.loadInitialData()
- }
- /**
- * 加载初始数据
- */
- private loadInitialData() {
- this.currentPage = 0
- this.hasMoreData = true
- this.updateListData(false)
- }
- /**
- * 搜索过滤歌曲
- */
- filterSongs() {
- this.isSearchMode = true
- if (!this.searchText.trim()) {
- this.filteredSongs = [...this.allSongs]
- } else {
- // 添加空值检查以防止TypeError
- if (!this.allSongs || !Array.isArray(this.allSongs)) {
- this.filteredSongs = []
- } else {
- this.filteredSongs = this.allSongs.filter(item => {
- //支持模糊匹配和艺术家 专辑匹配
- const regex = new RegExp(this.searchText.replace(/\s+/g, '.*'), 'i');
- return regex.test(item.name.toLowerCase()) ||
- regex.test(item.fileName?.toLowerCase() ?? "") ||
- regex.test(item.artist?.toLowerCase() ?? "") ||
- regex.test(item.album?.toLowerCase() ?? "")
- })
- }
- }
- // 重置分页状态并重新加载数据
- this.loadInitialData()
- }
- // 修改 updateListData 方法,使其更清晰
- updateListData(append: boolean = false) {
- this.getUIContext().animateTo({ duration: 666 }, () => {
- this.opacityItem = 0;
- })
- setTimeout(() => {
- const sourceData = this.filteredSongs
- let dataToShow: VideoItem[] = []
- if (append) {
- // 追加数据模式
- const currentData = this.dataSource.dataArray
- const startIndex = this.currentPage * this.PAGE_SIZE
- const endIndex = Math.min(startIndex + this.PAGE_SIZE, sourceData.length)
- const newData = sourceData.slice(startIndex, endIndex)
- if (newData.length > 0) {
- dataToShow = [...currentData, ...newData]
- this.currentPage++
- }
- } else {
- // 初始加载模式
- this.currentPage = 1
- const endIndex = Math.min(this.PAGE_SIZE, sourceData.length)
- dataToShow = sourceData.slice(0, endIndex)
- }
- // 更新数据源
- this.dataSource.pushArrayData(dataToShow)
- // 检查是否还有更多数据
- const totalLoaded = dataToShow.length
- this.hasMoreData = totalLoaded < sourceData.length
- this.getUIContext().animateTo({ duration: 666 }, () => {
- this.opacityItem = 1
- })
- }, 200)
- }
- loadMoreData() {
- console.info('loadMoreData called, hasMoreData:', this.hasMoreData, 'isLoading:', this.isLoading)
- if (!this.hasMoreData || this.isLoading) {
- console.info('loadMoreData skipped - no more data or already loading')
- return
- }
- console.info('loadMoreData executing')
- this.isLoading = true
- // 使用 setTimeout 模拟异步加载
- setTimeout(() => {
- this.updateListData(true) // append mode
- this.isLoading = false
- console.info('loadMoreData completed, current data length:', this.dataSource.dataArray.length)
- }, 50)
- }
- build() {
- Column({ space: 16 }) {
- // 搜索框
- Row({ space: 8 }) {
- Image($r('app.media.ic_action_search'))
- .width(20)
- .height(20)
- .fillColor(this.isDarkMode ? '#8E8E93' : $r('app.color.text_color'))
- .opacity(0.6)
- TextInput({ placeholder: '搜索歌曲、歌手或专辑', text: this.searchText })
- .layoutWeight(1)
- .height(35)
- .backgroundColor(this.isDarkMode ? '#2C2C2E' : $r('app.color.input_background'))
- .borderRadius(8)
- .padding({ left: 8, right: 8 })
- .fontSize(14)
- .fontColor(this.isDarkMode ? '#FFFFFF' : $r('app.color.text_color'))
- .placeholderColor(this.isDarkMode ? '#8E8E93' : '#999999')
- .onChange((value: string) => {
- this.searchText = value
- this.filterSongs()
- })
- }
- .width('100%')
- .padding(12)
- .backgroundColor(this.isDarkMode ? '#2C2C2E' : $r('app.color.input_background'))
- .borderRadius(20)
- // 已选择歌曲数量
- if (this.selectedSongs.length > 0) {
- Row() {
- Text(`已选择 ${this.selectedSongs.length} 首歌曲`)
- .fontSize(14)
- .fontColor(this.isDarkMode ? '#FFFFFF' : this.themeColor)
- .fontWeight(FontWeight.Medium)
- Blank()
- Button(this.selectedSongs.length === this.dataSource.dataArray.length
- && this.dataSource.dataArray.length > 0 ? '全不选' : '全选')
- .fontSize(12)
- .fontColor(this.isDarkMode ? '#FFFFFF' : $r('app.color.text_color'))
- .backgroundColor(Color.Transparent)
- .height(30)
- .padding({ left: 8, right: 8 })
- .onClick(() => {
- // 判断是否已经全选
- if (this.selectedSongs.length === this.dataSource.dataArray.length && this.dataSource.dataArray.length > 0) {
- // 如果已经全选,则清空选择
- this.selectedSongs = []
- } else {
- // 如果没有全选,则选择当前显示的所有歌曲
- this.selectedSongs = [...this.dataSource.dataArray]
- }
- })
- Button('清空')
- .fontSize(12)
- .fontColor(this.isDarkMode ? '#FFFFFF' : $r('app.color.text_color'))
- .backgroundColor(Color.Transparent)
- .height(30)
- .padding({ left: 8, right: 8 })
- .onClick(() => {
- this.selectedSongs = []
- })
- }
- .width('100%')
- .padding({ left: 4, right: 4 })
- }
- // 歌曲列表
- if (this.dataSource.dataArray.length === 0 && this.isSearchMode) {
- Column({ space: 12 }) {
- Image($r('app.media.music_red'))
- .width(64)
- .height(64)
- .opacity(0.3)
- Text(this.searchText ? '没有找到匹配的歌曲' : '没有可添加的歌曲')
- .fontSize(14)
- .fontColor(this.isDarkMode ? '#8E8E93' : '#999999')
- }
- .width('100%')
- .height(300)
- .justifyContent(FlexAlign.Center)
- } else {
- this.getListView()
- }
- // 按钮区域
- Row({ space: 12 }) {
- Button('取消')
- .width('45%')
- .height(40)
- .backgroundColor($r('app.color.cancel_button_background'))
- .borderRadius(8)
- .fontSize(14)
- .fontColor($r('app.color.cancel_button_text'))
- .onClick(() => {
- this.onCancel?.()
- DialogHelper.closeDialog('addSongsToPlaylistDialog')
- })
- Button(`添加${this.selectedSongs.length > 0 ? `(${this.selectedSongs.length})` : ''}`)
- .width('45%')
- .height(40)
- .backgroundColor(this.isDarkMode ? themeColorWithAlpha(this.themeColor, 0.8, this.isDarkMode) : this.themeColor)
- .borderRadius(8)
- .fontSize(14)
- .fontColor(Color.White)
- .enabled(this.selectedSongs.length > 0)
- .opacity(this.selectedSongs.length > 0 ? 1 : 0.5)
- .onClick(() => {
- this.handleConfirm()
- })
- }
- .width('100%')
- .justifyContent(FlexAlign.SpaceBetween)
- .margin({ top: 10, bottom: 10 })
- }
- .width('100%')
- .constraintSize({ maxWidth: 400 })
- .backgroundColor(this.isDarkMode ? '#2C2C2E' : $r('app.color.dialog_background'))
- .borderRadius(12)
- .padding({ left: 20, right: 20 })
- }
- /**
- * 处理确认操作
- */
- private handleConfirm() {
- if (this.selectedSongs.length === 0) {
- ToastUtil.showToast(' 请选择至少一首歌曲')
- return
- }
- this.onConfirm?.(this.selectedSongs)
- DialogHelper.closeDialog('addSongsToPlaylistDialog')
- }
- @Builder
- getListView() {
- Scroll() {
- Column({ space: 0 }) {
- List({ scroller: this.listScroller }) {
- LazyForEach(this.dataSource, (song: VideoItem, index: number) => {
- ListItem() {
- Row({ space: 12 }) {
- Image(StrUtil.isEmpty(song.pixelMapPath) ? $r('app.media.music_red') : song.pixelMapPath)
- .fillColor(StrUtil.isEmpty(song.pixelMapPath) ? (this.isDarkMode ? Color.White : this.themeColor) : undefined)
- .height(40)
- .width(40)
- .alt($r('app.media.music_red'))
- .borderRadius('100%')
- .clip(true)
- .draggable(false)
- .interpolation(ImageInterpolation.High)// 用于重采样后的抗锯齿
- .autoResize(true) // 重采样,可减少内存占用
- .opacity(this.opacityItem) // 绑定透明度
- // 歌曲信息
- Column({ space: 4 }) {
- Text(song.name || '未知歌曲')
- .fontSize(14)
- .fontColor(this.isDarkMode ? '#FFFFFF' : $r('app.color.text_color'))
- .maxLines(1)
- .textOverflow({ overflow: TextOverflow.Ellipsis })
- .width('100%')
- Row() {
- if (song.artist) {
- Text(song.artist+' '+song.duration)
- .fontSize(12)
- .fontColor(this.isDarkMode ? '#8E8E93' : '#999999')
- .maxLines(1)
- .textOverflow({ overflow: TextOverflow.Ellipsis })
- .layoutWeight(1)
- }
- }
- .width('100%')
- }
- .alignItems(HorizontalAlign.Start)
- .layoutWeight(1)
- // 选择框
- Checkbox({ name: 'song_' + song.id })
- .select(this.selectedSongs.some(s => s.id === song.id))
- .selectedColor($r('app.color.theme_color'))
- .shape(CheckBoxShape.ROUNDED_SQUARE)
- .onChange((checked: boolean) => {
- if (checked) {
- if (!this.selectedSongs.some(s => s.id === song.id)) {
- this.selectedSongs.push(song)
- }
- } else {
- const index = this.selectedSongs.findIndex(s => s.id === song.id)
- if (index > -1) {
- this.selectedSongs.splice(index, 1)
- }
- }
- })
- }
- .width('100%')
- .padding(12)
- .borderRadius(10)
- .onClick(() => {
- const isSelected = this.selectedSongs.some(s => s.id === song.id)
- if (isSelected) {
- const index = this.selectedSongs.findIndex(s => s.id === song.id)
- if (index > -1) {
- this.selectedSongs.splice(index, 1)
- }
- } else {
- this.selectedSongs.push(song)
- }
- })
- }
- .transition(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 1.2, y: 1.2 })
- .animation({ duration: 500 }),
- TransitionEffect.scale({ x: 0, y: 0 })))
- .clickEffect({ level: ClickEffectLevel.LIGHT })
- }, (song: VideoItem) => song.id)
- // 添加 footer 来显示加载状态
- ListItem() {
- this.footer()
- }
- .visibility(this.hasMoreData ? Visibility.Visible : Visibility.None)
- }
- .cachedCount(6)
- .height('100%')
- .scrollBar(BarState.Off)
- .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true })
- .onReachEnd(() => {
- // 滚动到底部时加载更多数据
- console.info('List onReachEnd triggered')
- this.loadMoreData()
- })
- }
- }
- .scrollBar(BarState.Auto)
- .scrollable(ScrollDirection.Vertical)
- .height(300)
- }
- // 改进的 footer Builder
- @Builder
- footer() {
- Column() {
- if (this.isLoading) {
- Row() {
- LoadingProgress()
- .height(20)
- .width(20)
- Text('加载中...')
- .fontSize(12)
- .fontColor(this.isDarkMode ? '#8E8E93' : '#999999')
- .margin({ left: 8 })
- }
- .width('100%')
- .height(40)
- .justifyContent(FlexAlign.Center)
- } else if (this.hasMoreData) {
- Row() {
- Text('上拉加载更多')
- .fontSize(12)
- .fontColor(this.isDarkMode ? '#8E8E93' : '#999999')
- }
- .width('100%')
- .height(40)
- .justifyContent(FlexAlign.Center)
- }
- }
- .width('100%')
- }
- }
- /**
- * 添加歌曲到歌单对话框管理器
- */
- @Component
- export struct AddSongsToPlaylistDialogManager {
- /**
- * 添加歌曲到歌单对话框构建器
- */
- @Builder
- buildAddSongsToPlaylistDialog(
- playlist: Playlist,
- onConfirm: (songs: VideoItem[]) => void,
- onCancel?: () => void,
- ) {
- AddSongsToPlaylistDialogContent({
- playlist: playlist,
- onConfirm: onConfirm,
- onCancel: onCancel
- })
- }
- /**
- * 显示添加歌曲到歌单对话框
- */
- showAddSongsToPlaylistDialog(
- playlist: Playlist,
- onConfirm: (songs: VideoItem[]) => void,
- onCancel?: () => void,
- ) {
- DialogHelper.showCustomContentDialog({
- dialogId: 'addSongsToPlaylistDialog',
- title: '添加歌曲到歌单',
- autoCancel: true,
- contentBuilder: () => {
- this.buildAddSongsToPlaylistDialog(playlist, onConfirm,onCancel)
- },
- buttons: []
- })
- }
- build() {
- }
- }
- // 创建全局实例
- const dialogManager = new AddSongsToPlaylistDialogManager()
- /**
- * 显示添加歌曲到歌单对话框
- */
- export function showAddSongsToPlaylistDialog(
- playlist: Playlist,
- onConfirm: (songs: VideoItem[]) => void,
- onCancel?: () => void,
- ) {
- dialogManager.showAddSongsToPlaylistDialog(playlist, onConfirm, onCancel)
- }
|