NewIndex.ets 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. import { AppUtil, DeviceUtil, DisplayUtil, LogUtil, PreferencesUtil, ToastUtil } from '@pura/harmony-utils';
  2. import TitleBar from '../view/TitleBar';
  3. import { curves, LengthMetrics, router, SegmentButton, SegmentButtonOptions } from '@kit.ArkUI';
  4. import mainViewModel, { MainViewModel } from '../viewmodel/MainViewModel';
  5. import ItemData from '../viewmodel/ItemData';
  6. import type { sysResource } from '../viewmodel/ItemData';
  7. import { CommonConstants } from '../common/constants/CommonConstants';
  8. import { VideoItem } from '../viewmodel/VideoItem';
  9. import ScreenUtil from '../common/util/ScreenUtil';
  10. import { Utility } from '../common/util/Utility';
  11. import { BusinessError, emitter } from '@kit.BasicServicesKit';
  12. import { common } from '@kit.AbilityKit';
  13. // import { AdSlot, AdSlotBuilder,
  14. // CSJAdCreator,
  15. // CSJAdSdk, CSJNativeExpressAd, NativeExpressAdListener } from '@csj/openadsdk';
  16. import { PrintBiddingTokenUtils } from '../common/util/PrintBiddintTokenUtils';
  17. import { ArrayList } from '@kit.ArkTS';
  18. import { CSJUtil } from '../common/util/CSJUtil';
  19. import { StreamContent } from '../view/StreamContent';
  20. import { AvSessionController } from '../controller/AvSessionController';
  21. import { BreakpointSystem, BreakpointTypeEnum } from '../common/util/BreakpointSystem';
  22. import dataStorage from '@ohos.data.storage';
  23. import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';
  24. import { SettingPage } from './SettingPage';
  25. import { deviceInfo } from '@kit.BasicServicesKit';
  26. import { resourceManager } from '@kit.LocalizationKit';
  27. import { systemShare } from '@kit.ShareKit';
  28. import { uniformTypeDescriptor } from '@kit.ArkData';
  29. import { hilog } from '@kit.PerformanceAnalysisKit';
  30. import { DialogHelper } from '@pura/harmony-dialog';
  31. import UserUtil from '../common/util/UserUtil';
  32. import json from '@ohos.util.json';
  33. import { UserCenter } from './UserCenter';
  34. import { ScanFilePage } from './ScanFilePage';
  35. import { AboutPage, gotoShare } from './AboutPage';
  36. import { smartMobilityCommon } from '@kit.CarKit';
  37. import { UpdateLogManager } from '../common/util/UpdateLogManager';
  38. import OnlineUpdateLogDialog from '../dialog/OnlineUpdateLog';
  39. import OnlineUpdateLog from '../dialog/OnlineUpdateLog';
  40. import { LocalMusic } from '../view/LocalMusic';
  41. import { ChartsCount } from './ChartsCount';
  42. import { image } from '@kit.ImageKit';
  43. // import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
  44. const TAG = 'NewIndex'; // 日志标签
  45. /**
  46. * 首页主组件,包含顶部标题栏、主内容区(本地音乐/网络内容)、侧边抽屉菜单等。
  47. * 支持多端适配、广告集成、导航、生命周期管理等功能。
  48. */
  49. @Preview
  50. @Entry
  51. @Component
  52. struct NewIndex {
  53. @State isDarkMode: boolean = false
  54. /** 列表滚动器,用于抽屉菜单列表滚动 */
  55. private scroller: Scroller = new Scroller();
  56. /** 应用包名 */
  57. @State bundleName: string = ''
  58. @State appName: string = ''
  59. /** 是否显示侧边抽屉菜单 */
  60. @Provide isShowDrawer: boolean = false;
  61. /** 抽屉菜单 X 轴偏移量,用于手势滑动动画 */
  62. @Provide offsetX: number = 0;
  63. /** 本地视频列表,页面参数传入 */
  64. @Provide videoLocalList: Array<VideoItem> = []
  65. /** Y 轴偏移量(预留) */
  66. @State offsetY: number = 0;
  67. /** 主内容类型(0:本地音乐,1:网络内容) */
  68. @Provide mType: number = 0
  69. /** 模式类型(如文件夹 媒体库 艺术家、专辑等) */
  70. @Provide modeType: number = 0
  71. /** 是否为零状态(预留) */
  72. @Provide('isZero') isZero: boolean = false;
  73. /** 是否显示赞助入口 */
  74. @State isShowSponsorship: boolean = false
  75. /** 页面上下文 */
  76. context = this.getUIContext().getHostContext() as common.UIAbilityContext
  77. /** 音乐是否为零状态(预留) */
  78. @Provide('isMusicZero') isMusicZero: boolean = false;
  79. @State isShowTitleBar: boolean = true //是否显示分类导航条
  80. /** 本地音乐列表,持久化存储 */
  81. @StorageLink('musicLocalList') musicLocalList: Array<VideoItem> = []
  82. @Provide isFavMusic: boolean = false
  83. @StorageProp('windowWidth') windowWidth: number = 0;
  84. @StorageProp('windowHeight') windowHeight: number = 0;
  85. @StorageProp('isHiCarStatus') isHiCarStatus: boolean = false;
  86. @StorageProp('curDisplayIsHiCar') curDisplayIsHiCar: boolean = false;
  87. @State idDefaultMediaKu: boolean = false
  88. /**
  89. * 是否显示更新日志开关
  90. */
  91. @State isShowUpdateDialog: boolean = false
  92. /** 标题栏配置模型 */
  93. @State titleBarModel: TitleBar.Model = new TitleBar.Model()
  94. .setTitleTextStyle(FontStyle.Normal)
  95. .setTitleBarStyle(TitleBar.BarStyle.TRANSPARENT)
  96. .setLeftIcon($r('app.media.menu'))
  97. .setLeftIconWidth(28)
  98. .setLeftIconHeight(28)
  99. .setTitleBarMinHeight(64)
  100. .setTitleName('首页')// .setRightIcon($r('app.media.add'))
  101. .setTitleFontSize(19)
  102. .setTitleFontColor(Color.White)
  103. .setTitleBarBackground($r('app.color.title_bar_bg'))
  104. .setLeftTitleBackground($r('app.color.title_bar_bg'))
  105. .setOnLeftClickListener(() => {
  106. // 打开菜单栏动画
  107. this.getUIContext()?.animateTo({ duration: 555 }, () => {
  108. this.isShowDrawer = true
  109. this.offsetX = 0
  110. })
  111. })
  112. .setRightTitleBackground($r('app.color.title_bar_bg'))
  113. // .setOnRightClickListener(()=>{
  114. // // this.goSelectVideo()
  115. // this.showSheelDialog()
  116. // })
  117. /** 应用版本号 */
  118. @State versionName: string = ''
  119. /** 音频根目录路径 */
  120. @Provide rootPath: string = ''
  121. /** 当前路径 */
  122. @Provide currentPath: string = ''
  123. /** 是否为历史记录页面 */
  124. @Provide isHistory: boolean = false
  125. /** 是否可以返回上一级 */
  126. @Provide isCanBack: boolean = false
  127. @StorageProp('currentColorMode') currentMode: number = ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
  128. @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
  129. /**
  130. * 一多界面适配断点系统
  131. */
  132. private breakpointSystem: BreakpointSystem = new BreakpointSystem();
  133. /** 当前断点类型(如大屏/小屏) */
  134. @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointTypeEnum.MD;
  135. /** 记录上一次点击返回键的时间戳,用于双击退出 */
  136. private backTime: number = 0;
  137. @State tabOptions: SegmentButtonOptions = SegmentButtonOptions.tab({
  138. buttons: [{ text: '分类' }, { text: '歌单' }],
  139. direction: Direction.Ltr,
  140. backgroundColor: $r('app.color.index_background'),
  141. selectedBackgroundColor:$r('app.color.start_window_background'),
  142. selectedFontColor: $r('app.color.text_color'),
  143. localizedTextPadding: {
  144. end: LengthMetrics.vp(10),
  145. start: LengthMetrics.vp(10)
  146. }
  147. })
  148. @State tabSelectedIndexes: number[] = [0]
  149. /**
  150. * 返回键处理逻辑:
  151. * - 如果不是根目录或有历史记录,发送广播通知更新列表
  152. * - 如果是根目录,双击返回键退出应用,否则提示
  153. */
  154. onBackPress(): boolean | void {
  155. if (this.currentPath !== this.rootPath || this.isHistory || this.isFavMusic ||
  156. (this.modeType !== 0 && this.isCanBack)) {
  157. console.info('onecold 返回键处理逻辑:发送音频广播通知更新列表');
  158. const eventData: emitter.EventData = {};
  159. emitter.emit({ eventId: 888 }, eventData); // 发送音频广播通知更新doSwipBack
  160. }else if(this.mType > 0){
  161. this.getUIContext()?.animateTo({ duration: 555 }, () => {
  162. // 动画闭包内控制Image组件的出现和消失
  163. // this.isShowDrawer = !this.isShowDrawer
  164. // this.offsetX = 0
  165. this.mType =0
  166. })
  167. } else {
  168. console.info('onecold 返回键处理 关闭');
  169. if (this.isShowDrawer) {
  170. // 如果抽屉菜单打开,先关闭抽屉
  171. this.getUIContext()?.animateTo({ duration: 555 }, () => {
  172. this.isShowDrawer = false
  173. })
  174. } else {
  175. return false
  176. }
  177. }
  178. return true;
  179. }
  180. /**
  181. * 刷新@State用户信息变量,确保UI同步
  182. */
  183. refreshUserInfoState() {
  184. this.isLogin = PreferencesUtil.getBooleanSync('isLogin', false);
  185. this.userId = PreferencesUtil.getNumberSync('userId', 0);
  186. this.userName = PreferencesUtil.getStringSync('userName', '未登录用户');
  187. this.userAvatarUrl = PreferencesUtil.getStringSync('userAvatarUrl', '');
  188. this.subscriptionName = PreferencesUtil.getStringSync('subscriptionName', '');
  189. this.subscriptionEndDate = PreferencesUtil.getStringSync('subscriptionEndDate', '');
  190. this.hasActiveSubscription = PreferencesUtil.getBooleanSync('hasActiveSubscription', false);
  191. }
  192. onPageShow() {
  193. }
  194. /**
  195. * 页面显示生命周期钩子
  196. * - 注册断点系统
  197. * - 获取页面参数
  198. * - 设置状态栏样式
  199. * - 初始化图片缓存
  200. * - 判断是否显示赞助入口
  201. */
  202. async aboutToAppear() {
  203. this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
  204. this.idDefaultMediaKu = PreferencesUtil.getBooleanSync('idDefaultMediaKu', false)
  205. if(this.idDefaultMediaKu){
  206. this.modeType = 1
  207. }
  208. Utility.enableFullScreen()
  209. this.topRectHeight = px2vp(AppUtil.getStatusBarHeight());
  210. Utility.getAppName(getContext(this)).then((appName: string) => {
  211. this.appName = appName
  212. })
  213. this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
  214. this.breakpointSystem.register();
  215. let params = router.getParams() as Record<string, Object>;
  216. this.videoLocalList = params.videoList as VideoItem[];
  217. // Utility.setStatusBarLight()
  218. ScreenUtil.setScreenSize();
  219. this.bundleName = AppUtil.getBundleName()
  220. this.versionName = AppUtil.getVersionName();
  221. // await ImageKnife.getInstance().initFileCache(this.context, 256, 256 * 1024 * 1024)
  222. this.isShowSponsorship = Utility.isOpenTime()
  223. let themeColor = PreferencesUtil.getStringSync('THEME_COLOR', CommonConstants.DEFAULT_THEME_COLOR);
  224. hilog.info(0x0000, 'Heanup', '当前 NewIndex themeColor:' + themeColor);
  225. AppStorage.setOrCreate('themeColor', themeColor);
  226. this.themeColor = themeColor
  227. // ====== 本地会员同步提醒逻辑 ======
  228. // this.refreshUserInfoState();
  229. // if (this.isLogin) {
  230. // void this.fetchUserInfo();
  231. // }
  232. console.log('Heanup isLogin:' + this.isLogin)
  233. console.log('Heanup Utility.isNobleForOld():' + Utility.isNobleForOld())
  234. await UserUtil.fetchUserInfo();
  235. this.refreshUserInfoState();
  236. let changeUserState: emitter.InnerEvent = { eventId: 1001 }
  237. emitter.on(changeUserState, () => {
  238. this.refreshUserInfoState();
  239. });
  240. let eventSetting: emitter.InnerEvent = { eventId: 333 }
  241. // 监听广播事件(通用设置配置更新)
  242. emitter.on(eventSetting, (eventData: emitter.EventData) => {
  243. this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
  244. });
  245. console.log('getHiCarStatus isHiCarStatus:' + this.isHiCarStatus)
  246. if(this.isHiCarStatus||this.isBigScreen()){
  247. this.getUIContext()?.animateTo({ duration: 555 }, () => {
  248. this.isShowDrawer = true
  249. this.offsetX = 0
  250. })
  251. this.isShowTitleBar = false
  252. }
  253. // 检查并显示更新日志
  254. this.checkAndShowUpdateLog();
  255. }
  256. /**
  257. * 检查并显示更新日志
  258. */
  259. private async checkAndShowUpdateLog() {
  260. try {
  261. const shouldShow = await UpdateLogManager.checkAndShowUpdateLog();
  262. if (shouldShow) {
  263. this.isShowUpdateDialog = !this.isShowUpdateDialog
  264. UpdateLogManager.markCurrentVersionShown()
  265. }
  266. // this.isShowUpdateDialog = true;//调试期间显示更新日志,测试完成后请删除
  267. } catch (error) {
  268. console.error('NewIndex checkAndShowUpdateLog error:', error);
  269. }
  270. }
  271. /**
  272. * 页面消失生命周期钩子
  273. * 注销断点系统
  274. */
  275. aboutToDisappear() {
  276. console.info('NewIndex aboutToDisappear');
  277. this.breakpointSystem.unregister();
  278. emitter.off(888);
  279. emitter.off(1001);
  280. }
  281. build() {
  282. SideBarContainer(SideBarContainerType.AUTO) {
  283. Column() {
  284. this.getLeftView()
  285. }
  286. .backgroundColor(Color.Transparent)
  287. Column() {
  288. Stack() {
  289. // 本地音乐内容区
  290. LocalMusic()
  291. .visibility(this.mType === 0 ? Visibility.Visible : Visibility.None)
  292. // 网络内容区
  293. UserCenter()
  294. .visibility(this.mType === 1 ? Visibility.Visible : Visibility.None)
  295. ScanFilePage()
  296. .visibility(this.mType === 2 ? Visibility.Visible : Visibility.None)
  297. ChartsCount({
  298. isShowDrawer:this.isShowDrawer,
  299. offsetX:this.offsetX
  300. })
  301. .visibility(this.mType === 3 ? Visibility.Visible : Visibility.None)
  302. SettingPage()
  303. .visibility(this.mType === 4 ? Visibility.Visible : Visibility.None)
  304. AboutPage()
  305. .visibility(this.mType === 5 ? Visibility.Visible : Visibility.None)
  306. // 抽屉打开时的遮罩层,用于拦截点击事件 这个只能正常尺寸的手机竖屏的才能生效
  307. if (this.isShowDrawer&&this.isPhonePortrait()) {
  308. Column()
  309. .width('100%')
  310. .height('100%')
  311. .backgroundBlurStyle(BlurStyle.BACKGROUND_THIN)
  312. .transition(TransitionEffect.OPACITY.animation({
  313. curve: curves.springMotion(0, 1)
  314. }))
  315. .onClick(() => {
  316. // 点击遮罩层关闭抽屉
  317. this.getUIContext()?.animateTo({ duration: 555 }, () => {
  318. this.isShowDrawer = false
  319. })
  320. })
  321. }
  322. }
  323. .bindSheet($$this.isShowUpdateDialog, this.updateSheet(), {
  324. height: '90%',
  325. dragBar: true,
  326. preferType: this.currentBreakpoint !== BreakpointTypeEnum.SM ? SheetType.CENTER : SheetType.BOTTOM,
  327. showClose: true,
  328. blurStyle: BlurStyle.Regular,
  329. title: { title: '更新日志' }
  330. })
  331. }
  332. .gesture(SwipeGesture({ direction: SwipeDirection.Horizontal }).onAction((event: GestureEvent) => {
  333. if (event) {//手势返回
  334. this.getUIContext()?.animateTo({ duration: 555 }, () => {
  335. this.isShowDrawer = !this.isShowDrawer
  336. if(this.isShowDrawer)
  337. this.offsetX = 0
  338. })
  339. }
  340. }))
  341. }
  342. .showControlButton(false)
  343. .minContentWidth(0)
  344. .sideBarWidth(260)
  345. .autoHide(true)
  346. .showSideBar($$this.isShowDrawer)
  347. .onChange((value: boolean) => {
  348. this.isShowDrawer = value
  349. })
  350. }
  351. //是不是hicar的屏幕分辨率
  352. isHiCar() {
  353. return this.isHiCarStatus&&this.curDisplayIsHiCar;
  354. // const hiCarAspectRatios: HiCarAspectRatio[] = [
  355. // { ratio: 800 / 480, name: "800x480" },
  356. // { ratio: 762 / 752, name: "762x752" },
  357. // { ratio: 968 / 1280, name: "968x1280" },
  358. // { ratio: 1200 / 1200, name: "1200x1200" },
  359. // { ratio: 1280 / 720, name: "1280x720" },
  360. // { ratio: 1920 / 1080, name: "1920x1080" }
  361. // ];
  362. // const currentRatio: number = this.windowWidth / this.windowHeight;
  363. // const RATIO_TOLERANCE: number = 0.1; // 宽高比容差
  364. //
  365. // for (const hiCarRatio of hiCarAspectRatios) {
  366. // if (Math.abs(currentRatio - hiCarRatio.ratio) <= RATIO_TOLERANCE) {
  367. // LogUtil.info(`HiCar detected: ${hiCarRatio.name}, actual: ${this.windowWidth}x${this.windowHeight}`);
  368. // return true;
  369. // }
  370. // }
  371. return false;
  372. }
  373. //是不是正常的手机竖屏
  374. isPhonePortrait() {
  375. LogUtil.info('twocold this.currentHeightBreakpoint = '+this.currentHeightBreakpoint)
  376. LogUtil.info('twocold this.currentWidthBreakpoint = '+this.currentWidthBreakpoint)
  377. if(DeviceUtil.getDeviceType() === resourceManager.DeviceType.DEVICE_TYPE_PHONE
  378. &&this.currentHeightBreakpoint == HeightBreakpoint.HEIGHT_LG
  379. &&this.currentWidthBreakpoint==WidthBreakpoint.WIDTH_SM){//如果是手机横屏,返回false
  380. return true
  381. }
  382. return false
  383. }
  384. /**
  385. * 判断是否横屏
  386. * @returns
  387. */
  388. isBigScreen() {
  389. if (this.currentWidthBreakpoint == WidthBreakpoint.WIDTH_LG) {
  390. return true
  391. }
  392. if (DeviceUtil.getDeviceType() === resourceManager.DeviceType.DEVICE_TYPE_PHONE
  393. && this.currentWidthBreakpoint === WidthBreakpoint.WIDTH_SM
  394. && this.currentHeightBreakpoint == HeightBreakpoint.HEIGHT_LG) {
  395. return false
  396. } else {
  397. return true
  398. }
  399. return false
  400. }
  401. @Builder
  402. getLeftView() {
  403. Column() {
  404. Column() {
  405. this.getDrawerView()
  406. }
  407. .width('100%')
  408. .height('100%')
  409. .onClick((event: ClickEvent) => {
  410. // 点击抽屉内部不关闭,通过返回值阻止事件传播
  411. return true
  412. })
  413. }
  414. // .width(this.currentBreakpoint !== BreakpointTypeEnum.SM?'36%':'70%')
  415. .height('100%')
  416. .borderRadius({
  417. topLeft: 0,
  418. topRight: 20,
  419. bottomLeft: 0,
  420. bottomRight: 20
  421. })
  422. // .backgroundColor($r('app.color.silvery'))
  423. // .backgroundColor($r('app.color.index_background'))
  424. .backgroundColor($r('app.color.user_center_card_background'))
  425. .alignItems(HorizontalAlign.Start)
  426. .translate({ x: this.offsetX, y: 0, z: 0 })
  427. .transition({ type: TransitionType.Insert, translate: { x: -DisplayUtil.getWidth(), y: 0 } })
  428. .transition({ type: TransitionType.Delete, translate: { x: -DisplayUtil.getWidth(), y: 0 } })
  429. .gesture(
  430. PanGesture()
  431. .onActionUpdate((event: GestureEvent) => {
  432. // 手势滑动更新抽屉偏移
  433. if (event.offsetX < 0) {
  434. this.offsetX = event.offsetX;
  435. }
  436. })
  437. .onActionEnd(() => {
  438. // 手势结束判断是否关闭抽屉
  439. if (this.offsetX < -DisplayUtil.getWidth() / 15) {
  440. this.getUIContext()?.animateTo({ duration: 555 }, () => {
  441. this.isShowDrawer = false
  442. })
  443. } else {
  444. this.getUIContext()?.animateTo({ duration: 555 }, () => {
  445. this.isShowDrawer = true
  446. this.offsetX = 0
  447. })
  448. }
  449. })
  450. )
  451. }
  452. @State isLogin: boolean = false;
  453. @State userAvatar: Resource = $r('app.media.icon_person2');
  454. @State userAvatarUrl: string = '';
  455. @State isVip: boolean = false;
  456. @State userName: string = '未登录用户';
  457. @State userId: number = 0;
  458. @State hasActiveSubscription: boolean = false;
  459. @State subscriptionName: string = '';
  460. @State subscriptionEndDate: string = '';
  461. // 用户信息卡片
  462. @Builder
  463. buildUserInfoCard() {
  464. Column() {
  465. this.userInfoView()
  466. Column({ space: 20 }) {
  467. SegmentButton({ options: this.tabOptions, selectedIndexes: $tabSelectedIndexes })
  468. }
  469. .padding({ bottom:10 })
  470. .width('80%')
  471. }
  472. }
  473. @Builder
  474. userInfoView() {
  475. Column() {
  476. Row() {
  477. Stack({ alignContent: Alignment.BottomEnd }){
  478. Image(this.userAvatarUrl && this.userAvatarUrl.length > 0 ? this.userAvatarUrl : this.userAvatar)
  479. .width(55)
  480. .height(55)
  481. .margin({ left: 12 })
  482. .borderRadius('50%')
  483. .clip(true)
  484. // .fillColor(this.themeColor)
  485. // .backgroundColor(this.isDarkMode ? Color.Black : Color.White)
  486. Column() {
  487. Text('VIP')
  488. .fontSize(9)
  489. .padding(2)
  490. .textAlign(TextAlign.Center)
  491. .fontWeight(FontWeight.Bolder)
  492. .fontColor(Color.White)
  493. }
  494. .width(28)
  495. .height(16)
  496. .visibility(Utility.isNoble()?Visibility.Visible:Visibility.None)
  497. .borderRadius(15)
  498. .backgroundColor(this.themeColor)
  499. }
  500. Column() {
  501. Row() {
  502. Text(this.isLogin ? this.userName : '未登录用户')
  503. .fontSize(14)
  504. // .fontWeight(FontWeight.Bold)
  505. // .fontColor(this.isDarkMode ? Color.White : Color.Black)
  506. .textAlign(TextAlign.Start)
  507. .maxLines(1)
  508. .width(110)
  509. .margin({ right: 12 })
  510. }
  511. .justifyContent(FlexAlign.Start)
  512. .margin({ top: 2 })
  513. if (this.isLogin) {
  514. if (this.hasActiveSubscription) {
  515. // Text(this.subscriptionName)
  516. // .fontSize(13)
  517. // .fontColor(themeColorWithAlpha(this.themeColor, 0.8, false))
  518. // .textAlign(TextAlign.Start)
  519. // .padding({top:5})
  520. this.TimeTextBuilder()
  521. } else {
  522. Text('普通用户')
  523. .fontSize(13)
  524. .fontColor(this.isDarkMode ? Color.White : Color.Grey)
  525. .textAlign(TextAlign.Start)
  526. .padding({top:5})
  527. this.TimeTextBuilder()
  528. }
  529. } else {
  530. this.TimeTextBuilder()
  531. }
  532. }
  533. .alignItems(HorizontalAlign.Start)
  534. .width(80)
  535. .margin({ left: 10 })
  536. }
  537. .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.7})
  538. .onClick(() => {
  539. // if (!this.isLogin) {
  540. this.mType = 1
  541. if (!this.isBigScreen()) {
  542. this.getUIContext()?.animateTo({ duration: 555 }, () => {
  543. this.isShowDrawer = false
  544. })
  545. // }
  546. }
  547. })
  548. .width('100%')
  549. .padding(10)
  550. }
  551. .backgroundColor($r('app.color.user_center_card_background'))
  552. .borderRadius(24)
  553. .width('86%')
  554. .padding(8)
  555. .margin({ left: 10, right: 10, top: this.topRectHeight + 10,bottom:10 })
  556. .shadow({
  557. radius: 8,
  558. color: 0x11000000,
  559. offsetX: 0,
  560. offsetY: 2
  561. })
  562. }
  563. @Builder
  564. TimeTextBuilder() {
  565. Text(Utility.getTimePeriod())
  566. .fontSize(13)
  567. .margin({ top: 5 })
  568. .fontColor($r('app.color.text_color'))
  569. .align(Alignment.Start)
  570. }
  571. /**
  572. * 动态生成抽屉菜单内容
  573. * 根据 isShowSponsorship 展示不同菜单项
  574. */
  575. @Builder
  576. getDrawerView() {
  577. List({ space: 0, scroller: this.scroller }) {
  578. ListItemGroup({ header: this.buildUserInfoCard() }) {
  579. if(this.tabSelectedIndexes[0]==0){//分类
  580. this.buildTabCate()
  581. }else{//歌单,这里写一个歌单的listItem
  582. }
  583. }
  584. }
  585. .scrollBar(BarState.Off)
  586. .width('86%')
  587. .borderRadius(20)
  588. .margin({
  589. bottom: 20,
  590. left: 10,
  591. right: 10,
  592. top: 10
  593. })
  594. .alignListItem(ListItemAlign.Center)
  595. .layoutWeight(1)
  596. .backgroundColor($r('app.color.left_draw_bg'))
  597. .edgeEffect(EdgeEffect.None) // 必须设置列表为滑动到边缘无效果
  598. }
  599. @Builder
  600. buildTabCate() {
  601. ForEach(this.isHiCar()||!this.isShowTitleBar?mainViewModel.getDrawerData():mainViewModel.getDrawerData2(), (item: ItemData, index: number) => {
  602. ListItem() {
  603. Button({ type: ButtonType.Capsule, stateEffect: true }) {
  604. Row() {
  605. // 菜单图标
  606. if (typeof item.img === 'object' && item.img !== null && (item.img as sysResource).type === 'symbol') {
  607. SymbolGlyph((item.img as sysResource).value as Resource)// .size({ width: 22, height: 22 })
  608. .fontSize(22)
  609. .fontColor([this.themeColor])
  610. .effectStrategy(SymbolEffectStrategy.HIERARCHICAL)
  611. .alignSelf(ItemAlign.Center)
  612. .margin({ left: 25 })
  613. } else {
  614. Image(item.img as Resource)
  615. .height(22)
  616. .alignSelf(ItemAlign.Center)
  617. .fillColor(this.themeColor)
  618. .margin({ left: 25 })
  619. }
  620. // 菜单标题
  621. Text(item.title)
  622. .margin({ left: 10, right: 20 })
  623. .fontSize(15)
  624. .fontColor(this.isTextSelected(index) ? this.themeColor : $r('app.color.index_tab_font_color'))
  625. .fontWeight(480)
  626. Blank()
  627. // 右侧箭头
  628. Image($r('app.media.arrow_right'))
  629. .width(22)
  630. .height(22)
  631. .margin({ left: 20, right: 0 })
  632. .align(Alignment.Center)
  633. }
  634. .width('100%')
  635. .height(55)
  636. }
  637. .backgroundColor(Color.Transparent)
  638. .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
  639. .onClick(async () => {
  640. // 根据 item.id 跳转或切换功能
  641. switch (item.id) {
  642. case MainViewModel.MENU_MUSIC:
  643. this.mType = 0
  644. this.modeType = 0
  645. this.doShowDrawer()
  646. break
  647. case MainViewModel.MENU_FILE_SCAN:
  648. this.mType = 2
  649. this.doShowDrawer()
  650. break
  651. case MainViewModel.MENU_CHARTS:
  652. this.mType = 3
  653. this.modeType = 0
  654. this.doShowDrawer()
  655. break
  656. case MainViewModel.MENU_MIEDIA_KU:
  657. this.modeType = 1
  658. this.mType = 0
  659. this.doShowDrawer()
  660. break
  661. case MainViewModel.MENU_MIEDIA_ARTIST:
  662. this.modeType = 2
  663. this.mType = 0
  664. this.doShowDrawer()
  665. break
  666. case MainViewModel.MENU_MIEDIA_ALBUM:
  667. this.modeType = 3
  668. this.mType = 0
  669. this.doShowDrawer()
  670. break
  671. case MainViewModel.MENU_SETTING:
  672. this.mType = 4
  673. this.doShowDrawer()
  674. break
  675. case MainViewModel.MENU_VIP:
  676. router.pushUrl({
  677. url: 'pages/VipPage'
  678. }, router.RouterMode.Single);
  679. break
  680. case MainViewModel.MENU_USER:
  681. this.mType = 1
  682. this.doShowDrawer()
  683. break
  684. case MainViewModel.MENU_NET_CONNECT:
  685. this.mType = 1
  686. this.doShowDrawer()
  687. break
  688. case MainViewModel.MENU_SIMI:
  689. router.pushUrl({
  690. url: 'pages/VerifyPage'
  691. });
  692. break
  693. case MainViewModel.MENU_DUTY:
  694. router.pushUrl({
  695. url: 'pages/WebIndex',
  696. params: { titleName: '用户协议', webUrl: CommonConstants.NEW_DUTY }
  697. });
  698. break
  699. case MainViewModel.MENU_HAOPING:
  700. Utility.gotoMarket(getContext(this) as common.UIAbilityContext, this.bundleName)
  701. break
  702. case MainViewModel.MENU_ABOUT:
  703. this.mType = 5
  704. this.doShowDrawer()
  705. break
  706. case MainViewModel.MENU_UPDATE:
  707. AlertDialog.show({
  708. title: '版本更新',
  709. message: '当前版本为最新版本:' + this.versionName,
  710. autoCancel: true,
  711. alignment: DialogAlignment.Center,
  712. offset: { dx: 0, dy: -20 }, //在Y轴方向上的编译量
  713. confirm: {
  714. value: '确定',
  715. fontColor: Color.White,
  716. backgroundColor: $r('app.color.title_bar_bg'),
  717. action: () => {
  718. }
  719. }
  720. })
  721. break
  722. case MainViewModel.MENU_YSZC:
  723. router.pushUrl({
  724. url: 'pages/WebIndex',
  725. params: { titleName: '隐私政策', webUrl: CommonConstants.NEW_YS_HW }
  726. });
  727. break
  728. case MainViewModel.MENU_SHARE:
  729. gotoShare(this.context,this.appName,this.bundleName)
  730. break
  731. }
  732. })
  733. }
  734. .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
  735. .animation({ duration: 600, curve: Curve.Ease, delay: 60 * index }))
  736. .width('90%')
  737. .height(55)
  738. })
  739. }
  740. isTextSelected(index:number):boolean{
  741. if(this.isShowTitleBar&&!this.isHiCar()){
  742. return this.mType ==index
  743. }else{
  744. if(this.mType == 0){
  745. return this.modeType == index
  746. }
  747. return this.mType+3 == index
  748. }
  749. return false
  750. }
  751. doShowDrawer(){
  752. if (!this.isBigScreen()) {
  753. this.getUIContext()?.animateTo({ duration: 555 }, () => {
  754. this.isShowDrawer = false
  755. })
  756. }
  757. }
  758. @Builder
  759. updateSheet() {
  760. Scroll() {
  761. Column() {
  762. OnlineUpdateLog()
  763. }
  764. }
  765. .width('100%')
  766. .height('100%')
  767. }
  768. // /**
  769. // * ================== 穿山甲广告相关 ==================
  770. // */
  771. // /** Banner广告对象 */
  772. // private declare bannerAd: CSJNativeExpressAd;
  773. // /** 广告加载状态 */
  774. // @State private status: string = "未加载"
  775. // /** 是否展示广告 */
  776. // @State private isShowAd: boolean = false
  777. // /** 广告位配置信息 */
  778. // private declare mAdSlot: AdSlot;
  779. // /** 服务端bidding广告内容(可选) */
  780. // private mBiddingAdm = ''
  781. // /** 广告加载监听器 */
  782. // private expressLoadAdListener: NativeExpressAdListener = {
  783. // /**
  784. // * 广告加载失败回调
  785. // */
  786. // onError: (code: number, message: string) => {
  787. // console.error("加载广告失败,code=" + code + ",message=" + message);
  788. // this.status = "加载广告失败,code=" + code + ",message=" + message;
  789. // },
  790. // /**
  791. // * 广告加载成功回调
  792. // * @param ads 返回的广告列表
  793. // */
  794. // onNativeExpressAdLoad: (ads: ArrayList<CSJNativeExpressAd>) => {
  795. // console.log("BannerExpressAdPage==onNativeExpressAdLoad......success");
  796. // if (ads && ads.length > 0) {
  797. // ads.forEach((ad: CSJNativeExpressAd, idx: number) => {
  798. // // 设置广告交互监听
  799. // ad.setExpressInteractionListener({
  800. // /** 广告点击回调 */
  801. // onAdClicked: (type: number) => {
  802. // console.log("BannerExpressAdPage==onAdClicked......");
  803. // },
  804. // /** 广告展示回调 */
  805. // onAdShow: (type: number) => {
  806. // console.log("BannerExpressAdPage==onAdShow......");
  807. // },
  808. // /** 模板渲染失败回调 */
  809. // onRenderFail: (code: number, msg: string) => {
  810. // console.log("BannerExpressAdPage==onRenderFail...code=" + code + ",msg=" + msg);
  811. // },
  812. // /** 模板渲染成功回调 */
  813. // onRenderSuccess: (width: number, height: number) => {
  814. // console.log("BannerExpressAdPage==onRenderSuccess......width=" + width + ",height=" + height);
  815. // this.bannerAd = ad;
  816. // this.status = "广告加载完成待展示";
  817. // this.showBannerAd()
  818. // }
  819. // })
  820. // // 设置广告轮播时间
  821. // ad.setSlideIntervalTime(30 * 1000);//设置轮播时间长
  822. // this.setDislikeCallback(ad); //设置dislike
  823. // ad.render(this.getUIContext()) // 开始渲染广告
  824. // this.status = "广告加载中...";
  825. // });
  826. // }
  827. // }
  828. // }
  829. //
  830. // /**
  831. // * 设置广告 dislike 回调
  832. // * @param ad 广告对象
  833. // */
  834. // private setDislikeCallback(ad: CSJNativeExpressAd) {
  835. // ad.setDislikeCallback({
  836. // /** dislike 弹窗显示 */
  837. // onShow: () => {
  838. // console.log("BannerExpressAdPage==dislike......show");
  839. // },
  840. // /** 选择 dislike 选项 */
  841. // onSelected: (position: number, value: string, enforceRemove: boolean) => {
  842. // this.isShowAd = false;
  843. // console.log("BannerExpressAdPage==dislike......onSelected:position=" + position + ",value:" + value + ",enforce=" + enforceRemove);
  844. // },
  845. // /** 点击取消 dislike */
  846. // onCancel: () => {
  847. // console.log("BannerExpressAdPage==dislike......onCancel");
  848. // }
  849. // })
  850. // }
  851. //
  852. // /**
  853. // * 加载Banner广告
  854. // * @param rit 广告位ID
  855. // */
  856. // loadBannerAd(rit: string) {
  857. // // 仅在满足条件时加载广告
  858. // if(!Utility.isNoble()){
  859. // return
  860. // }
  861. // if(!Utility.isPassInstallTime(2)){
  862. // return
  863. // }
  864. // this.isShowAd = false;
  865. // let adCreator:CSJAdCreator = CSJAdSdk.getAdCreator()
  866. // this.mAdSlot = new AdSlotBuilder()
  867. // .setCodeId(rit)
  868. // .setAcceptSize(CSJUtil.BANNER_WIGTH, CSJUtil.BANNER_HEIGHT)
  869. // .setAdCount(1)
  870. // .setBidAdm(this.mBiddingAdm)
  871. // .build()
  872. // PrintBiddingTokenUtils.printBiddingToken(this.mAdSlot, adCreator);
  873. // adCreator.loadBannerAd(this.mAdSlot, this.expressLoadAdListener)
  874. // this.status = "广告加载中..."
  875. // }
  876. @StorageProp('currentHeightBreakpoint') currentHeightBreakpoint: HeightBreakpoint | undefined =
  877. HeightBreakpoint.HEIGHT_LG;
  878. @StorageProp('currentWidthBreakpoint') currentWidthBreakpoint: WidthBreakpoint | undefined = WidthBreakpoint.WIDTH_SM;
  879. @StorageProp('topRectHeight') topRectHeight: number = px2vp(AppUtil.getStatusBarHeight());
  880. //是否是Pura X外屏,或者小屏幕
  881. isPuraWP() {
  882. return this.currentWidthBreakpoint === WidthBreakpoint.WIDTH_SM
  883. && this.currentHeightBreakpoint === HeightBreakpoint.HEIGHT_MD
  884. }
  885. //是否是Pura X外屏,或者小屏幕或者手机横屏
  886. isCoverOpacity() {
  887. if (this.isPuraWP()) {
  888. return true
  889. }
  890. if (this.isPhoneLan()) {
  891. return true
  892. }
  893. if (deviceInfo.marketName.includes('Pura X')
  894. && this.currentHeightBreakpoint === HeightBreakpoint.HEIGHT_SM) {
  895. return true
  896. }
  897. return false
  898. }
  899. //是不是手机横屏
  900. isPhoneLan() {
  901. if (DeviceUtil.getDeviceType() === resourceManager.DeviceType.DEVICE_TYPE_PHONE
  902. && this.currentBreakpoint !== BreakpointTypeEnum.SM) {
  903. if (DisplayUtil.getFoldStatus() === 0) {
  904. return true
  905. }
  906. }
  907. return false
  908. }
  909. isLoginChange() {
  910. this.refreshUserInfoState();
  911. }
  912. }
  913. // 1. 工具函数:将 #RRGGBB 字符串和透明度转为 'rgba(r,g,b,a)' 字符串,深色模式下可返回深色变体
  914. function themeColorWithAlpha(themeColor: string, alpha: number, isDarkMode: boolean = false): string {
  915. if (isDarkMode) {
  916. // 深色模式下返回更深的灰色或半透明黑色
  917. return `rgba(34,34,34,${alpha + 0.2 > 1 ? 1 : alpha + 0.2})`;
  918. }
  919. const color = themeColor.replace('#', '');
  920. const r = parseInt(color.substring(0, 2), 16);
  921. const g = parseInt(color.substring(2, 4), 16);
  922. const b = parseInt(color.substring(4, 6), 16);
  923. return `rgba(${r},${g},${b},${alpha})`;
  924. }
  925. // 定义接口
  926. interface HiCarAspectRatio {
  927. ratio: number;
  928. name: string;
  929. }