|
@@ -0,0 +1,482 @@
|
|
|
|
|
+import { common, ConfigurationConstant } from '@kit.AbilityKit';
|
|
|
|
|
+import { CommonConstants } from '../common/constants/CommonConstants';
|
|
|
|
|
+import { VideoItem } from '../viewmodel/VideoItem';
|
|
|
|
|
+import { LazyDataSource } from '../common/util/LazyDataSource';
|
|
|
|
|
+import { LogUtil, PreferencesUtil, StrUtil, ToastUtil } from '@pura/harmony-utils';
|
|
|
|
|
+import fs from '@ohos.file.fs';
|
|
|
|
|
+import { changeMusicCover, findLocalCoverImage, getApiLyric, repairAudioMetadata,
|
|
|
|
|
+ searchCover,
|
|
|
|
|
+ syncLyricToDB} from '../common/util/MusicTagUtils';
|
|
|
|
|
+import { util } from '@kit.ArkTS';
|
|
|
|
|
+import { FFMpegTags } from '../common/util/Utility';
|
|
|
|
|
+
|
|
|
|
|
+//批量编辑标签
|
|
|
|
|
+// 批量编辑标签
|
|
|
|
|
+@Component
|
|
|
|
|
+export struct TagsContentCover {
|
|
|
|
|
+ private listScroller: ListScroller = new ListScroller()
|
|
|
|
|
+ onTagsResult = (_result: boolean) => {
|
|
|
|
|
+ }
|
|
|
|
|
+ @State isLyric:boolean = true
|
|
|
|
|
+ @State isCover:boolean = true
|
|
|
|
|
+ @Link isShowDrawer: boolean;
|
|
|
|
|
+ @State dataSource: LazyDataSource<VideoItem> = new LazyDataSource([])
|
|
|
|
|
+ @Prop selectedFiles: Array<VideoItem>
|
|
|
|
|
+ @StorageProp('topRectHeight') topRectHeight: number = 0;
|
|
|
|
|
+ @State isDarkMode: boolean = false
|
|
|
|
|
+ @StorageProp('currentColorMode') @Watch('onColorModeChange') currentMode: number =
|
|
|
|
|
+ ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
|
|
|
|
|
+ @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
|
|
|
|
|
+ context = this.getUIContext().getHostContext() as common.UIAbilityContext
|
|
|
|
|
+ // 新增状态用于进度显示
|
|
|
|
|
+ @State currentProgress: number = 0;
|
|
|
|
|
+ @State totalFiles: number = 0;
|
|
|
|
|
+ @State currentFileIndex: number = 0;
|
|
|
|
|
+ @State currentFileName: string = '';
|
|
|
|
|
+ @State isEmbedding: boolean = false;
|
|
|
|
|
+ @State embedSuccessCount: number = 0;
|
|
|
|
|
+ @State embedFailedCount: number = 0;
|
|
|
|
|
+
|
|
|
|
|
+ // 新增状态用于跟踪每个文件的嵌入状态
|
|
|
|
|
+ @State embedStatusMap: Map<string, boolean> = new Map<string, boolean>();
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ onColorModeChange() {
|
|
|
|
|
+ this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async aboutToAppear() {
|
|
|
|
|
+ // 确保 selectedFiles 有默认值后再初始化 dataSource
|
|
|
|
|
+ if (this.selectedFiles && this.selectedFiles.length > 0) {
|
|
|
|
|
+ this.dataSource = new LazyDataSource(this.selectedFiles)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.dataSource = new LazyDataSource([])
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ build() {
|
|
|
|
|
+ Column(){
|
|
|
|
|
+ this.topTitleBar()
|
|
|
|
|
+ this.TagSetting()
|
|
|
|
|
+ this.startButton()
|
|
|
|
|
+ this.listView()
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .backgroundColor($r('app.color.start_window_background'))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ listView() {
|
|
|
|
|
+ Column(){
|
|
|
|
|
+ List({ scroller: this.listScroller }) {
|
|
|
|
|
+ ListItemGroup({ header: this.listHeader() }) {
|
|
|
|
|
+ LazyForEach(this.dataSource, (item: VideoItem, index: number) => {
|
|
|
|
|
+ ListItem() {
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ this.MusicItem(item, index)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .transition(TransitionEffect.asymmetric(
|
|
|
|
|
+ TransitionEffect.scale({ x: 1.2, y: 1.2 }).animation({ duration: 500 }),
|
|
|
|
|
+ TransitionEffect.scale({ x: 0, y: 0 })
|
|
|
|
|
+ ))
|
|
|
|
|
+ .clickEffect({ level: ClickEffectLevel.LIGHT })
|
|
|
|
|
+ }, (item: VideoItem) => item.filePath)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ .cachedCount(2)
|
|
|
|
|
+ // .layoutWeight(1)
|
|
|
|
|
+ // .height('100%')
|
|
|
|
|
+ .transition(TransitionEffect.asymmetric(
|
|
|
|
|
+ TransitionEffect.scale({ x: 1.2, y: 1.2 }).animation({ duration: 500 }),
|
|
|
|
|
+ TransitionEffect.scale({ x: 0, y: 0 })
|
|
|
|
|
+ ))
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ .borderRadius(20)
|
|
|
|
|
+ .padding({top:10,bottom:6})
|
|
|
|
|
+ .height('auto')
|
|
|
|
|
+ .backgroundColor($r('app.color.index_background'))
|
|
|
|
|
+ .margin({top:20,right:20,left:20})
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ listHeader() {
|
|
|
|
|
+ Stack({alignContent:Alignment.Start}){
|
|
|
|
|
+ Text(`共${this.selectedFiles.length}首歌`)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .textAlign(TextAlign.Start)
|
|
|
|
|
+ .padding({ left: 25 })
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .margin({bottom:6,top:4,left: 6 })
|
|
|
|
|
+ }
|
|
|
|
|
+ .height('auto').width('100%')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ TagSetting() {
|
|
|
|
|
+ Column(){
|
|
|
|
|
+
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ SymbolGlyph($r('sys.symbol.doc_text'))
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+ .fontColor([this.themeColor])
|
|
|
|
|
+ .alignSelf(ItemAlign.Center)
|
|
|
|
|
+ .margin({ left: 15 })
|
|
|
|
|
+ Text('内嵌歌词')
|
|
|
|
|
+ .margin({ left: 8 })
|
|
|
|
|
+ .fontSize(15)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .fontWeight(480)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ Toggle({ type: ToggleType.Switch, isOn: this.isLyric })
|
|
|
|
|
+ .selectedColor(this.themeColor)
|
|
|
|
|
+ .switchPointColor(Color.White)
|
|
|
|
|
+ .margin({ right: 18 })
|
|
|
|
|
+ .onChange((checked: boolean) => {
|
|
|
|
|
+ this.isLyric = checked;
|
|
|
|
|
+ })
|
|
|
|
|
+ .width(50)
|
|
|
|
|
+ .height(30);
|
|
|
|
|
+ }
|
|
|
|
|
+ .height(55)
|
|
|
|
|
+ .clickEffect({level:ClickEffectLevel.HEAVY,scale:0.6})
|
|
|
|
|
+
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ SymbolGlyph($r('sys.symbol.picture'))
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+ .fontColor([this.themeColor])
|
|
|
|
|
+ .alignSelf(ItemAlign.Center)
|
|
|
|
|
+ .margin({ left: 15 })
|
|
|
|
|
+ Text('内嵌封面')
|
|
|
|
|
+ .margin({ left: 8 })
|
|
|
|
|
+ .fontSize(15)
|
|
|
|
|
+ .fontColor($r('app.color.text_color'))
|
|
|
|
|
+ .fontWeight(480)
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ Toggle({ type: ToggleType.Switch, isOn: this.isCover })
|
|
|
|
|
+ .selectedColor(this.themeColor)
|
|
|
|
|
+ .switchPointColor(Color.White)
|
|
|
|
|
+ .margin({ right: 18 })
|
|
|
|
|
+ .onChange((checked: boolean) => {
|
|
|
|
|
+ this.isCover = checked;
|
|
|
|
|
+ })
|
|
|
|
|
+ .width(50)
|
|
|
|
|
+ .height(30);
|
|
|
|
|
+ }
|
|
|
|
|
+ .height(55)
|
|
|
|
|
+ .clickEffect({level:ClickEffectLevel.HEAVY,scale:0.6})
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ .borderRadius(20)
|
|
|
|
|
+ .backgroundColor($r('app.color.index_background'))
|
|
|
|
|
+ .margin({top:20,right:20,left:20})
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ startButton(){
|
|
|
|
|
+ Stack(){
|
|
|
|
|
+ Progress({ value: this.currentProgress, total: this.totalFiles,
|
|
|
|
|
+ type: ProgressType.Capsule }).height(55)
|
|
|
|
|
+ .margin({top:20,right:20,left:20})
|
|
|
|
|
+ .backgroundColor($r('app.color.index_background'))
|
|
|
|
|
+ Button({ type: ButtonType.Capsule, stateEffect: true }) {
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ // 菜单图标
|
|
|
|
|
+ SymbolGlyph($r('sys.symbol.star_trophy'))// .size({ width: 22, height: 22 })
|
|
|
|
|
+ .fontSize(22)
|
|
|
|
|
+ .fontColor([this.themeColor])
|
|
|
|
|
+ .alignSelf(ItemAlign.Center)
|
|
|
|
|
+ .margin({ left: 25 })
|
|
|
|
|
+
|
|
|
|
|
+ // 菜单标题
|
|
|
|
|
+ Text('开始内嵌')
|
|
|
|
|
+ .margin({ left: 10, right: 20 })
|
|
|
|
|
+ .fontSize(15)
|
|
|
|
|
+ .fontWeight(480)
|
|
|
|
|
+ Blank()
|
|
|
|
|
+ // 右侧箭头
|
|
|
|
|
+ Image($r('app.media.arrow_right'))
|
|
|
|
|
+ .width(22)
|
|
|
|
|
+ .height(22)
|
|
|
|
|
+ .margin({ left: 0, right: 20 })
|
|
|
|
|
+ .align(Alignment.Center)
|
|
|
|
|
+ }
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ }
|
|
|
|
|
+ .margin({top:20,right:20,left:20})
|
|
|
|
|
+ .height(55)
|
|
|
|
|
+ .enabled(!this.isEmbedding)
|
|
|
|
|
+ .backgroundColor(Color.Transparent)
|
|
|
|
|
+ .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
|
|
|
|
|
+ .onClick(()=>{
|
|
|
|
|
+ this.startEmbed()
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 开始嵌入封面和歌词
|
|
|
|
|
+ */
|
|
|
|
|
+ async startEmbed() {
|
|
|
|
|
+ if (this.isEmbedding || !this.selectedFiles || this.selectedFiles.length === 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.isEmbedding = true;
|
|
|
|
|
+ this.currentProgress = 0;
|
|
|
|
|
+ this.currentFileIndex = 0;
|
|
|
|
|
+ this.embedSuccessCount = 0;
|
|
|
|
|
+ this.embedFailedCount = 0;
|
|
|
|
|
+ this.totalFiles = this.selectedFiles.length;
|
|
|
|
|
+
|
|
|
|
|
+ // 清空之前的嵌入状态
|
|
|
|
|
+ this.embedStatusMap = new Map<string, boolean>();
|
|
|
|
|
+
|
|
|
|
|
+ // 创建异步任务数组
|
|
|
|
|
+ const embedTasks = this.selectedFiles.map(async (item: VideoItem, index: number) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.currentFileIndex = index + 1;
|
|
|
|
|
+ this.currentFileName = item.name;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ let success = true;
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 处理封面 (如果启用且需要处理)
|
|
|
|
|
+ if(this.isCover){
|
|
|
|
|
+ let defaultCover = ''
|
|
|
|
|
+ // 先查找同名的png或jpg文件
|
|
|
|
|
+ if (StrUtil.isEmpty(item.pixelMapPath) && item.filePath) {
|
|
|
|
|
+ defaultCover = findLocalCoverImage(item.filePath);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有找到同名图片文件,则进行在线查询
|
|
|
|
|
+ if (StrUtil.isEmpty(defaultCover)) {
|
|
|
|
|
+ const imagePath: string | undefined = await searchCover(
|
|
|
|
|
+ this.context,
|
|
|
|
|
+ item,
|
|
|
|
|
+ item.name,
|
|
|
|
|
+ item?.artist || ''
|
|
|
|
|
+ );
|
|
|
|
|
+ defaultCover = imagePath || '';
|
|
|
|
|
+ }
|
|
|
|
|
+ }else if(item.pixelMapPath&&item.pixelMapPath.startsWith('http')){
|
|
|
|
|
+ defaultCover = item.pixelMapPath
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StrUtil.isNotEmpty(defaultCover)) {
|
|
|
|
|
+ // 确保 filePath 存在
|
|
|
|
|
+ if (item.filePath) {
|
|
|
|
|
+ const result = await changeMusicCover(
|
|
|
|
|
+ this.context,
|
|
|
|
|
+ item.filePath,
|
|
|
|
|
+ defaultCover,
|
|
|
|
|
+ true // 覆盖原文件
|
|
|
|
|
+ );
|
|
|
|
|
+ if (!result) {
|
|
|
|
|
+ success = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ success = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 处理歌词 (如果启用)
|
|
|
|
|
+ if (this.isLyric) {
|
|
|
|
|
+ let lyricContent = item.lyricContent || ''; // 默认为空字符串
|
|
|
|
|
+
|
|
|
|
|
+ // 如果歌词为空,尝试读取同名.lrc文件
|
|
|
|
|
+ if (StrUtil.isEmpty(lyricContent) && item.filePath) {
|
|
|
|
|
+ const lrcPath = item.filePath.substring(0, item.filePath.lastIndexOf('.')) + '.lrc';
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (fs.accessSync(lrcPath)) {
|
|
|
|
|
+ const file = fs.openSync(lrcPath, fs.OpenMode.READ_ONLY);
|
|
|
|
|
+ const buf = new ArrayBuffer(102400); // 100KB缓冲区
|
|
|
|
|
+ const len = fs.readSync(file.fd, buf);
|
|
|
|
|
+ const textDecoder = new util.TextDecoder('utf-8');
|
|
|
|
|
+ // 修复错误3: 使用 Uint8Array 包装 ArrayBuffer
|
|
|
|
|
+ lyricContent = textDecoder.decode(new Uint8Array(buf.slice(0, len)));
|
|
|
|
|
+ fs.closeSync(file);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.warn(`读取LRC文件失败: ${JSON.stringify(error)}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果仍然为空,尝试从API获取歌词
|
|
|
|
|
+ if (StrUtil.isEmpty(lyricContent) && item.filePath) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const apiUrl = PreferencesUtil.getStringSync('LRC_API', '');
|
|
|
|
|
+ // 修复错误1: 添加非空检查
|
|
|
|
|
+ if (StrUtil.isNotEmpty(apiUrl)) {
|
|
|
|
|
+ lyricContent = await getApiLyric(
|
|
|
|
|
+ apiUrl,
|
|
|
|
|
+ item.name || '', // 修复可能为undefined的情况
|
|
|
|
|
+ item.artist || '', // 修复可能为undefined的情况
|
|
|
|
|
+ apiUrl.includes(CommonConstants.LRC_API_2)
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.warn(`获取API歌词失败: ${JSON.stringify(error)}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果有歌词内容,嵌入到音频文件
|
|
|
|
|
+ if (StrUtil.isNotEmpty(lyricContent) && item.filePath) {
|
|
|
|
|
+ const metadata: FFMpegTags = {};
|
|
|
|
|
+ // 修复错误2: 添加非空检查
|
|
|
|
|
+ const result = await repairAudioMetadata(
|
|
|
|
|
+ item.filePath,
|
|
|
|
|
+ lyricContent,
|
|
|
|
|
+ metadata,
|
|
|
|
|
+ true // 覆盖原文件
|
|
|
|
|
+ );
|
|
|
|
|
+ if (!result) {
|
|
|
|
|
+ success = false;
|
|
|
|
|
+ }else{
|
|
|
|
|
+ //内嵌成功后同步歌词到数据库
|
|
|
|
|
+ syncLyricToDB(this.context,item.filePath,lyricContent)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新嵌入状态
|
|
|
|
|
+ if (item.filePath) {
|
|
|
|
|
+ this.embedStatusMap.set(item.filePath, success);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (success) {
|
|
|
|
|
+ this.embedSuccessCount++;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.embedFailedCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 滚动到当前处理的项目位置
|
|
|
|
|
+ // this.listScroller.scrollToIndex(index);
|
|
|
|
|
+ this.currentProgress = Math.floor(((index + 1) / this.totalFiles) * 100);
|
|
|
|
|
+ return success;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error(`处理文件 ${item.name} 失败: ${JSON.stringify(error)}`);
|
|
|
|
|
+ this.embedFailedCount++;
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 并发执行所有嵌入任务
|
|
|
|
|
+ await Promise.all(embedTasks);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ this.onTagsResult(false)
|
|
|
|
|
+ console.error(`批量嵌入过程出错: ${JSON.stringify(error)}`);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ ToastUtil.showToast(`嵌入完成: 成功 ${this.embedSuccessCount}, 失败 ${this.embedFailedCount}`)
|
|
|
|
|
+ this.isEmbedding = false;
|
|
|
|
|
+ this.currentProgress = 0;
|
|
|
|
|
+ this.totalFiles = this.selectedFiles.length;
|
|
|
|
|
+ this.onTagsResult(true)
|
|
|
|
|
+ // 可以在这里添加完成后的提示或回调
|
|
|
|
|
+ console.info(`嵌入完成: 成功 ${this.embedSuccessCount}, 失败 ${this.embedFailedCount}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ topTitleBar() {
|
|
|
|
|
+ // 顶部安全区和自定义标题栏
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ // 顶部安全区
|
|
|
|
|
+ Blank()
|
|
|
|
|
+ .height(this.topRectHeight)
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
|
|
|
|
|
+ .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
|
|
|
|
|
+ // 自定义标题栏(Stack实现绝对居中)
|
|
|
|
|
+ Stack() {
|
|
|
|
|
+ // 居中标题
|
|
|
|
|
+ Text('批量内嵌标签')
|
|
|
|
|
+ .fontSize(18)
|
|
|
|
|
+ .fontColor(Color.White)
|
|
|
|
|
+ .align(Alignment.Center)
|
|
|
|
|
+ // 左右按钮
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ Image($r('app.media.left_back_white'))
|
|
|
|
|
+ .width(26)
|
|
|
|
|
+ .height(26)
|
|
|
|
|
+ .margin({ left: 12, right: 8 })
|
|
|
|
|
+ .onClick(() => {
|
|
|
|
|
+ this.getUIContext().animateTo({ duration: 666 }, () => {
|
|
|
|
|
+ // 动画闭包内控制Image组件的出现和消失
|
|
|
|
|
+ this.isShowDrawer = !this.isShowDrawer
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ Blank().flexGrow(1)
|
|
|
|
|
+ Blank().width(32)
|
|
|
|
|
+ }
|
|
|
|
|
+ .height(48)
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .alignItems(VerticalAlign.Center)
|
|
|
|
|
+ }
|
|
|
|
|
+ .height(48)
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ private MusicItem(item: VideoItem, index?: number) {
|
|
|
|
|
+ Button({ type: ButtonType.Normal, stateEffect: true }) {
|
|
|
|
|
+ Column() {
|
|
|
|
|
+ Row() {
|
|
|
|
|
+ SymbolGlyph($r('sys.symbol.music'))
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+ .fontColor([this.themeColor])
|
|
|
|
|
+ .alignSelf(ItemAlign.Center)
|
|
|
|
|
+ .padding({ left: 25 })
|
|
|
|
|
+ Text(item.name)
|
|
|
|
|
+ .fontSize(14)
|
|
|
|
|
+ .maxLines(1)
|
|
|
|
|
+ .padding({ left: 5 })
|
|
|
|
|
+ .textOverflow({ overflow: TextOverflow.MARQUEE })
|
|
|
|
|
+ .animation({
|
|
|
|
|
+ duration: 555,
|
|
|
|
|
+ curve: 'Linear',
|
|
|
|
|
+ })
|
|
|
|
|
+ .fontColor(Color.Gray)
|
|
|
|
|
+ .margin({ left: 8 })
|
|
|
|
|
+ Blank()
|
|
|
|
|
+ // 根据嵌入状态显示勾选图标
|
|
|
|
|
+ SymbolGlyph($r('sys.symbol.checkmark_circle'))
|
|
|
|
|
+ .fontSize(20)
|
|
|
|
|
+ .fontColor([this.themeColor])
|
|
|
|
|
+ .alignSelf(ItemAlign.Center)
|
|
|
|
|
+ .padding({ right: 25 })
|
|
|
|
|
+ .animation({
|
|
|
|
|
+ duration: 666,
|
|
|
|
|
+ curve: 'ease-in-out' // 可选动画曲线
|
|
|
|
|
+ })
|
|
|
|
|
+ .visibility(this.embedStatusMap.get(item.filePath)?Visibility.Visible:Visibility.None)
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ .layoutWeight(1)
|
|
|
|
|
+ .height('100%')
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .backgroundColor(Color.Transparent)
|
|
|
|
|
+ .width('100%')
|
|
|
|
|
+ .height(40)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|