chendeben hace 1 año
padre
commit
8ccf8d6880

+ 5 - 5
build-profile.json5

@@ -27,13 +27,13 @@
         "name": "default",
         "type": "HarmonyOS",
         "material": {
-          "certpath": "C:\\Users\\xjy08\\.ohos\\config\\default_com.xgplayer.ttmusic.hm4_li00v0NJ2KHEZXa_MEhSBo4Q7SMa2KHpzKM8lnBZT1Y=.cer",
+          "certpath": "C:\\Users\\Admin\\.ohos\\config\\default_TTMusic_4xE8H3uDhF_yQsHvKPeuQ4Ihr8SeH-FhaPc2QJXUNQQ=.cer",
           "keyAlias": "debugKey",
-          "keyPassword": "0000001BE60CC62B8E573007038FACF6CFC47BFE4E4E984B54575F173B76B39D947CAD63C9FFE3D521CFDE",
-          "profile": "C:\\Users\\xjy08\\.ohos\\config\\default_com.xgplayer.ttmusic.hm4_li00v0NJ2KHEZXa_MEhSBo4Q7SMa2KHpzKM8lnBZT1Y=.p7b",
+          "keyPassword": "0000001AB642934648E4D9123518000E574FF5EC00A13E807027B43264E70DDE8B55883D4BACDEA39DC0",
+          "profile": "C:\\Users\\Admin\\.ohos\\config\\default_TTMusic_4xE8H3uDhF_yQsHvKPeuQ4Ihr8SeH-FhaPc2QJXUNQQ=.p7b",
           "signAlg": "SHA256withECDSA",
-          "storeFile": "C:\\Users\\xjy08\\.ohos\\config\\default_com.xgplayer.ttmusic.hm4_li00v0NJ2KHEZXa_MEhSBo4Q7SMa2KHpzKM8lnBZT1Y=.p12",
-          "storePassword": "0000001BD9A69C06F9FF60724E3E60B60A59A39DA090E83D4DDBCA419D296CBAB038F8AC930C36C897090C"
+          "storeFile": "C:\\Users\\Admin\\.ohos\\config\\default_TTMusic_4xE8H3uDhF_yQsHvKPeuQ4Ihr8SeH-FhaPc2QJXUNQQ=.p12",
+          "storePassword": "0000001A6A7B4454B87E22F2B10AE207891C8683F4985FB806FEA863564DC5AC0268C5DA8C17EEF45B4F"
         }
       }
     ]

+ 2 - 0
entry/src/main/ets/Constants.ets

@@ -0,0 +1,2 @@
+export const APP_ID = "wx263d1c80d204c3c0"
+export const APP_SECRET = "a29d0d08885334589698837e46d4573a"

+ 1 - 1
entry/src/main/ets/pages/AboutPage.ets

@@ -157,4 +157,4 @@ export struct AboutPage{
       .linearGradient(!this.isDarkMode ? {colors:[['#FF4081', 0.0], ['#FF8C00', 0.6]]} : undefined)
     }
   }
-}
+}    

+ 7 - 1
entry/src/main/ets/pages/NewIndex.ets

@@ -172,6 +172,7 @@ async aboutToAppear() {
   this.versionName =  AppUtil.getVersionName();
   await ImageKnife.getInstance().initFileCache(this.context, 256, 256 * 1024 * 1024)
   this.isShowSponsorship = Utility.isOpenTime()
+
 }
 
   /**
@@ -231,7 +232,7 @@ async aboutToAppear() {
             }
             .width(this.currentBreakpoint !== BreakpointTypeEnum.SM?'40%':'75%')
             .height('100%')
-          .backgroundColor($r('app.color.silvery'))
+            .backgroundColor($r('app.color.silvery'))
             .onClick((event:ClickEvent)=>{
               // 阻止事件冒泡
             })
@@ -358,6 +359,11 @@ async aboutToAppear() {
                   url: 'pages/VipPage'
                 }, router.RouterMode.Single);
                 break
+              case MainViewModel.MENU_USER:
+                router.pushUrl({
+                  url: 'pages/UserCenter'
+                }, router.RouterMode.Single);
+                break
               case MainViewModel.MENU_NET_CONNECT:
                 this.mType = 1
                 animateTo({ duration: 555 }, () => {

+ 364 - 0
entry/src/main/ets/pages/UserCenter.ets

@@ -0,0 +1,364 @@
+import TitleBar from '../view/TitleBar';
+import { router } from '@kit.ArkUI';
+import { CommonConstants } from '../common/constants/CommonConstants';
+import { Utility } from '../common/util/Utility';
+import { AppUtil, DeviceUtil, DisplayUtil, LogUtil, ToastUtil, PreferencesUtil } from '@pura/harmony-utils';
+import { resourceManager } from '@kit.LocalizationKit';
+import { BreakpointTypeEnum } from '../common/util/BreakpointSystem';
+import * as wxopensdk from '@tencent/wechat_open_sdk';
+import { OnWXResp, WXApi, WXEventHandler } from '../common/util/WXApiWrap';
+import { common } from '@kit.AbilityKit';
+import { ErrCode } from '@tencent/wechat_open_sdk';
+import { http } from '@kit.NetworkKit';
+
+interface WechatUserInfo {
+  openid?: string;
+  nickname?: string;
+  sex?: number;
+  language?: string;
+  city?: string;
+  province?: string;
+  country?: string;
+  headimgurl?: string;
+  privilege?: string[];
+  unionid?: string;
+}
+
+interface Subscription {
+  plan_id: string;
+  plan_name: string;
+  plan_description: string;
+  price: string;
+  start_date: string;
+  end_date: string;
+  status: string;
+  auto_renew: string;
+  features: string[];
+}
+
+interface SubscriptionInfo {
+  has_active_subscription: boolean;
+  subscriptions: Subscription[];
+}
+
+interface responseData {
+  token_info?: object;
+  user_info: WechatUserInfo;
+}
+
+interface GeneratedTypeLiteralInterface_1 {
+  token_info?: object;
+  user_info: WechatUserInfo;
+  system_info?: object;
+  subscription_info?: SubscriptionInfo;
+}
+
+interface WechatUserInfoApiResponse {
+  code: number;
+  msg: string;
+  data: GeneratedTypeLiteralInterface_1;
+}
+
+@Component
+@Entry
+export struct UserCenter {
+  @State userName: string = '未登录用户';
+  @State userAvatar: Resource = $r('app.media.icon_person2');
+  @State userAvatarUrl: string = '';
+  @State isVip: boolean = false;
+  @State vipExpire: string = '';
+  @State isLogin: boolean = false;
+  @State hasActiveSubscription: boolean = false;
+  @State subscriptionName: string = '';
+  @State subscriptionEndDate: string = '';
+  private wxApi = WXApi
+  private wxEventHandler = WXEventHandler
+  @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointTypeEnum.MD;
+  @State titleBarModel: TitleBar.Model = new TitleBar.Model()
+    .setTitleTextStyle(FontStyle.Normal)
+    .setTitleBarStyle(TitleBar.BarStyle.TRANSPARENT)
+    .setLeftIcon($r('app.media.left_back_white'))
+    .setTitleName('用户中心')
+    .setTitleFontColor(Color.White)
+    .setTitleBarBackground($r('app.color.title_bar_bg'))
+    .setLeftTitleBackground($r('app.color.title_bar_bg'))
+    .setTitleBarBottomLineColor($r('app.color.title_bar_bg'))
+    .setOnLeftClickListener(() => {
+      router.back();
+    });
+
+  // 微信回调处理
+  private onWXResp: OnWXResp = (resp) => {
+    let authResultText = JSON.stringify(resp ?? {}, null, 2)
+    if (resp.errCode === ErrCode.ERR_OK) {
+      // 提取code并请求用户信息
+        this.getUserInfoByCode(resp?.['code'])
+      ToastUtil.showToast('登录成功!')
+    } else {
+      ToastUtil.showToast('微信登录失败!')
+    }
+    console.log('微信回调结果:' + authResultText)
+  }
+
+  // 通过code换取用户信息
+  async getUserInfoByCode(code: string) {
+    try {
+      const url = `https://pay.ss5.xyz/wechat/user_info?code=${encodeURIComponent(code)}`;
+      const httpRequest = http.createHttp();
+      const options: http.HttpRequestOptions = {
+        method: http.RequestMethod.GET,
+        readTimeout: 3000,
+        connectTimeout: 3000,
+      };
+      const response: http.HttpResponse = await httpRequest.request(url, options);
+      if (response.responseCode === 200) {
+        let res = response.result as string;
+        LogUtil.debug("getUserInfoByCode res =" + res)
+        let json: WechatUserInfoApiResponse = JSON.parse(res) as WechatUserInfoApiResponse;
+        if (json.code === 0 && json.data && json.data.user_info) {
+          let userInfo: WechatUserInfo = json.data.user_info;
+          this.isLogin = true;
+          this.userName = userInfo.nickname || '微信用户';
+          if (userInfo.headimgurl && userInfo.headimgurl.length > 0) {
+            this.userAvatarUrl = userInfo.headimgurl;
+            this.userAvatar = $r('app.media.icon_person2');
+          } else {
+            this.userAvatarUrl = '';
+            this.userAvatar = $r('app.media.icon_person2');
+          }
+          // 解析订阅信息
+          const subInfo = json.data.subscription_info;
+          if (subInfo && subInfo.has_active_subscription && subInfo.subscriptions && subInfo.subscriptions.length > 0) {
+            this.hasActiveSubscription = true;
+            this.subscriptionName = subInfo.subscriptions[0].plan_name;
+            this.subscriptionEndDate = subInfo.subscriptions[0].end_date;
+          } else {
+            this.hasActiveSubscription = false;
+            this.subscriptionName = '';
+            this.subscriptionEndDate = '';
+          }
+          // 保存登录状态和用户信息
+          PreferencesUtil.putSync('isLogin', true);
+          PreferencesUtil.putSync('userName', this.userName);
+          PreferencesUtil.putSync('userAvatarUrl', this.userAvatarUrl);
+          PreferencesUtil.putSync('subscriptionName', this.subscriptionName);
+          PreferencesUtil.putSync('subscriptionEndDate', this.subscriptionEndDate);
+          PreferencesUtil.putSync('hasActiveSubscription', this.hasActiveSubscription);
+          ToastUtil.showToast('微信登录成功');
+        } else {
+          ToastUtil.showToast('用户信息解析失败');
+        }
+      } else {
+        ToastUtil.showToast('获取用户信息失败');
+      }
+    } catch (e) {
+      ToastUtil.showToast('登录失败');
+    }
+  }
+
+  aboutToAppear() {
+    // 自动恢复本地登录状态
+    this.isLogin = PreferencesUtil.getBooleanSync('isLogin', false);
+    this.userName = PreferencesUtil.getStringSync('userName', '未登录用户');
+    this.userAvatarUrl = PreferencesUtil.getStringSync('userAvatarUrl', '');
+    this.subscriptionName = PreferencesUtil.getStringSync('subscriptionName', '');
+    this.subscriptionEndDate = PreferencesUtil.getStringSync('subscriptionEndDate', '');
+    this.hasActiveSubscription = PreferencesUtil.getBooleanSync('hasActiveSubscription', false);
+    this.wxEventHandler.registerOnWXRespCallback(this.onWXResp)
+    // 这里可以加载用户信息
+    // this.isLogin = ...
+    // this.userName = ...
+    // this.userAvatar = ...
+    // this.isVip = ...
+    // this.vipExpire = ...
+  }
+
+  aboutToDisappear() {
+    this.wxEventHandler.unregisterOnWXRespCallback(this.onWXResp)
+  }
+
+  build() {
+    Column() {
+      // 顶部安全区和标题栏
+      Column() {
+        Line().width('100%').height('100%')
+      }
+      .height(this.isPhoneLan()?0:px2vp(AppUtil.getStatusBarHeight()))
+      .backgroundColor($r('app.color.title_bar_bg'))
+      .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
+      TitleBar({ model: this.titleBarModel })
+
+      // 用户信息卡片
+      Column() {
+        // 头像居中
+        Row() {
+          Blank().layoutWeight(1)
+          Image(this.userAvatarUrl && this.userAvatarUrl.length > 0 ? this.userAvatarUrl : this.userAvatar)
+            .width(80)
+            .height(80)
+            .borderRadius('50%')
+            .clip(true)
+            .backgroundColor(Color.White)
+            .shadow({ radius: 8, color: 0x11000000, offsetX: 0, offsetY: 2 })
+          Blank().layoutWeight(1)
+        }
+        .margin({ top: 32, bottom: 12 })
+
+        // 昵称
+        Text(this.isLogin ? this.userName : '未登录用户')
+          .fontSize(22)
+          .fontWeight(FontWeight.Bold)
+          .fontColor(Color.Black)
+          .margin({ bottom: 4 })
+          .alignSelf(ItemAlign.Center)
+
+        // 身份信息
+        if (this.isLogin) {
+          if (this.hasActiveSubscription) {
+            Text(this.subscriptionName)
+              .fontSize(16)
+              .fontColor(Color.Red)
+              .alignSelf(ItemAlign.Center)
+            Text('到期:' + this.subscriptionEndDate)
+              .fontSize(14)
+              .fontColor(Color.Gray)
+              .alignSelf(ItemAlign.Center)
+          } else {
+            Text('普通用户')
+              .fontSize(15)
+              .fontColor(Color.Grey)
+              .alignSelf(ItemAlign.Center)
+          }
+        } else {
+          Text('请先登录')
+            .fontSize(15)
+            .fontColor(Color.Grey)
+            .alignSelf(ItemAlign.Center)
+        }
+      }
+      .backgroundColor(Color.White)
+      .borderRadius(24)
+      .margin({ left: 20, right: 20, top: 20, bottom: 0 })
+      .shadow({ radius: 12, color: 0x11000000, offsetX: 0, offsetY: 2 })
+
+      // 操作列表
+      Column() {
+        this.menuItem($r('app.media.icon_favorite'), '我的收藏', () => {
+          if (!this.isLogin) {
+            ToastUtil.showToast('请先登录')
+            return
+          }
+          ToastUtil.showToast('我的收藏')
+        })
+        Line().width('90%').height(0.5).backgroundColor(0xFFEFEFEF).alignSelf(ItemAlign.Center)
+        this.menuItem($r('app.media.icon_history'), '历史记录', () => {
+          if (!this.isLogin) {
+            ToastUtil.showToast('请先登录')
+            return
+          }
+          ToastUtil.showToast('历史记录')
+        })
+        Line().width('90%').height(0.5).backgroundColor(0xFFEFEFEF).alignSelf(ItemAlign.Center)
+        this.menuItem($r('app.media.icon_person2'), '联系客服', () => {
+          Utility.copyText(CommonConstants.CONTACT_MAIL)
+          ToastUtil.showToast('已复制客服邮箱')
+        })
+      }
+      .backgroundColor(Color.White)
+      .borderRadius(24)
+      .margin({ left: 20, right: 20, top: 24, bottom: 0 })
+      .shadow({ radius: 8, color: 0x11000000, offsetX: 0, offsetY: 2 })
+
+      // 退出登录/登录按钮
+      Row() {
+        Blank().layoutWeight(1)
+        if (this.isLogin) {
+          Button('退出登录')
+            .fontColor(Color.White)
+            .backgroundColor(Color.Red)
+            .height(48)
+            .width('70%')
+            .borderRadius(24)
+            .onClick(() => {
+              this.isLogin = false
+              this.userName = '未登录用户'
+              this.userAvatar = $r('app.media.icon_person2')
+              this.userAvatarUrl = ''
+              this.isVip = false
+              this.vipExpire = ''
+              this.hasActiveSubscription = false;
+              this.subscriptionName = '';
+              this.subscriptionEndDate = '';
+              PreferencesUtil.putSync('isLogin', false);
+              PreferencesUtil.putSync('userName', '未登录用户');
+              PreferencesUtil.putSync('userAvatarUrl', '');
+              PreferencesUtil.putSync('subscriptionName', '');
+              PreferencesUtil.putSync('subscriptionEndDate', '');
+              PreferencesUtil.putSync('hasActiveSubscription', false);
+              ToastUtil.showToast('已退出登录')
+              // 这里可以添加退出登录逻辑
+            })
+        } else {
+          Button('登录')
+            .fontColor(Color.White)
+            .backgroundColor($r('app.color.title_bar_bg'))
+            .height(48)
+            .width('70%')
+            .borderRadius(24)
+            .onClick(async () => {
+              let req = new wxopensdk.SendAuthReq
+              req.isOption1 = false
+              req.nonAutomatic = true
+              req.scope = 'snsapi_userinfo,snsapi_friend,snsapi_message,snsapi_contact'
+              req.state = 'none'
+              req.transaction = 'test123'
+              let finished = await this.wxApi.sendReq(getContext(this) as common.UIAbilityContext, req)
+              console.log('send request finished: ', finished)
+            })
+        }
+        Blank().layoutWeight(1)
+      }
+      .margin({ top: 48, bottom: 0 })
+    }
+    .backgroundColor(0xFFF6F7FA)
+    .width('100%')
+    .height('100%')
+  }
+
+  isPhoneLan() {
+    if (DeviceUtil.getDeviceType() === resourceManager.DeviceType.DEVICE_TYPE_PHONE
+      && this.currentBreakpoint !== BreakpointTypeEnum.SM) {
+      if (DisplayUtil.getFoldStatus() === 0 || DisplayUtil.getFoldStatus() === 2) {
+        return true
+      }
+    }
+
+    return false
+  }
+
+  // 菜单项构建
+  @Builder
+  menuItem(icon: Resource, text: string, onClick: () => void) {
+    Row() {
+      Image(icon)
+        .width(22)
+        .height(22)
+        .alignSelf(ItemAlign.Center)
+        .margin({ left: 22 })
+      Text(text)
+        .margin({ left: 10, right: 20 })
+        .fontSize(15)
+        .fontColor(Color.Gray)
+        .fontWeight(480)
+      Blank()
+      Image($r('app.media.arrow_right'))
+        .width(22)
+        .height(22)
+        .margin({ left: 20, right: 20 })
+        .align(Alignment.Center)
+    }
+    .width('100%')
+    .height(50)
+    .onClick(onClick)
+  }
+}

+ 2 - 0
entry/src/main/ets/viewmodel/MainViewModel.ets

@@ -29,6 +29,7 @@ export  class  MainViewModel{
   static readonly MENU_OPTIMIZE: number = 88;
   static readonly MENU_ABOUT: number = 99;
   static readonly MENU_UPDATE: number = 111;
+  static readonly MENU_USER: number = 112;
   static readonly MENU_YSZC: number = 222;
 
   static readonly MENU_DUTY: number = 221;
@@ -68,6 +69,7 @@ export  class  MainViewModel{
     let drawerGridData: ItemData[] = [
 
       new ItemData($r('app.string.local_music'), $r('app.media.music_menu'),MainViewModel.MENU_MUSIC,false),
+      new ItemData($r('app.string.user_center'), $r('app.media.ic_public_user'),MainViewModel.MENU_USER,false),
       // new ItemData($r('app.string.media_ku'), $r('app.media.hm_playlist'),MainViewModel.MENU_MIEDIA_KU,false),
       // new ItemData($r('app.string.artist'), $r('app.media.kp_music'),MainViewModel.MENU_MIEDIA_ARTIST,false),
       // new ItemData($r('app.string.album'), $r('app.media.llq'),MainViewModel.MENU_MIEDIA_ALBUM,false),

+ 4 - 0
entry/src/main/module.json5

@@ -23,6 +23,10 @@
       {
         "name": "ArkTSPartialUpdate",
         "value": "true"
+      },
+      {
+        "name": "client_id",
+        "value": "6917560202517406526"
       }
     ],
 

+ 4 - 0
entry/src/main/resources/base/element/string.json

@@ -427,6 +427,10 @@
     {
       "name": "rename",
       "value": "重命名"
+    },
+    {
+      "name": "user_center",
+      "value": "用户中心"
     }
   ]
 }

BIN
entry/src/main/resources/base/media/ic_public_user.png


BIN
entry/src/main/resources/base/media/icon_favorite.png


BIN
entry/src/main/resources/base/media/icon_history.png


+ 2 - 1
entry/src/main/resources/base/profile/main_pages.json

@@ -10,6 +10,7 @@
     "pages/SettingPage",
     "pages/IjkMusicPlayerPage",
     "pages/VipPage",
-    "pages/ScanFilePage"
+    "pages/ScanFilePage",
+    "pages/UserCenter"
   ]
 }

+ 4 - 0
entry/src/main/resources/zh_CN/element/string.json

@@ -199,6 +199,10 @@
     {
       "name": "current_play_list",
       "value": "播放列表"
+    },
+    {
+      "name": "reason_copymusic",
+      "value": ""
     }
   ]
 }

+ 7 - 0
oh_modules/@seagazer/cclyric/src/main/ets/parse/LyricParser.ts

@@ -28,6 +28,7 @@ export class LyricParser implements IParser {
         let album = ""
         let by = ""
         let offset = 0
+        const ignoredTags = [ 'hash', 'sign', 'qq', 'total']; // 定义需要忽略的标签
         for (let i = 0; i < src.length; i++) {
             let line = src[i]
             console.info(`content The line of file:line ${line}`);
@@ -36,6 +37,12 @@ export class LyricParser implements IParser {
                 printW("the lyric line is empty, carriage return or line feed, line index= " + i)
                 continue
             }
+            // 检查是否是需要忽略的标签
+            const shouldIgnore = ignoredTags.some(tag  => line.indexOf(tag)  > 0);
+            if (shouldIgnore) {
+                printW(`the lyric line contains ignored tag, line index= ${i}`);
+                continue;
+            }
             if (line.indexOf("ti") > 0) {
                 title = this.parseIdTag(line)
             } else if (line.indexOf("ar") > 0) {