UserCenter.ets 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216
  1. import { router } from '@kit.ArkUI';
  2. import { AppUtil, ToastUtil, PreferencesUtil, StrUtil } from '@pura/harmony-utils';
  3. import { BreakpointTypeEnum } from '../common/util/BreakpointSystem';
  4. import * as wxopensdk from '@tencent/wechat_open_sdk';
  5. import { OnWXResp, WXApi, WXEventHandler } from '../common/util/WXApiWrap';
  6. import { bundleManager, common, ConfigurationConstant } from '@kit.AbilityKit';
  7. import { ErrCode } from '@tencent/wechat_open_sdk';
  8. import { http } from '@kit.NetworkKit';
  9. import { authentication } from '@kit.AccountKit';
  10. import { hilog } from '@kit.PerformanceAnalysisKit';
  11. import { BusinessError, emitter } from '@kit.BasicServicesKit';
  12. import { util } from '@kit.ArkTS';
  13. import { DialogHelper } from '@pura/harmony-dialog';
  14. import { Pay } from '@cashier_alipay/cashiersdk';
  15. import { OrderInfoUtil } from '../alipay/OrderInfoUtil';
  16. import { RandomUtil, Base64Util } from '@pura/harmony-utils';
  17. import { CommonConstants } from '../common/constants/CommonConstants';
  18. import { PayReq } from '@tencent/wechat_open_sdk';
  19. import { cryptoFramework } from '@kit.CryptoArchitectureKit';
  20. import json from '@ohos.util.json';
  21. import UserUtil, { VipFeature, VipPlanApi, VipPlanListApiResponse, WeChatPrepayId } from '../common/util/UserUtil';
  22. import { Utility } from '../common/util/Utility';
  23. import { pinyin4js } from '@ohos/pinyin4js';
  24. // 微信支付相关工具方法
  25. async function isHasWX(): Promise<boolean> {
  26. try {
  27. let link = 'weixin://';
  28. let data = bundleManager.canOpenLink(link);
  29. if (data) {
  30. return true;
  31. } else {
  32. ToastUtil.showToast('微信未安装');
  33. return false;
  34. }
  35. } catch (err) {
  36. let message = (err as BusinessError).message;
  37. console.error('canOpenLink failed: %{public}s', message);
  38. return false;
  39. }
  40. }
  41. async function createWeChatOrder(app_id: string, mch_id: string, description: string, amount: number,
  42. out_trade_no_pre: string, uid: number, plan_id: string): Promise<string> {
  43. let requestUrl = CommonConstants.WX_PAY_API
  44. + '?app_id=' + encodeURIComponent(app_id.trim())
  45. + '&mch_id=' + encodeURIComponent(mch_id.trim())
  46. + '&description=' + encodeURIComponent(description.trim())
  47. + '&amount=' + encodeURIComponent(amount)
  48. + '&uid=' + encodeURIComponent(uid)
  49. + '&plan_id=' + encodeURIComponent(plan_id)
  50. + '&out_trade_no_pre=' + encodeURIComponent(out_trade_no_pre);
  51. const httpRequest: http.HttpRequest = http.createHttp();
  52. const options: http.HttpRequestOptions = {
  53. method: http.RequestMethod.GET,
  54. readTimeout: 6000,
  55. connectTimeout: 6000,
  56. };
  57. try {
  58. const response: http.HttpResponse = await httpRequest.request(requestUrl, options);
  59. if (response.responseCode === 200) {
  60. let responseData: WeChatPrepayId;
  61. if (typeof response.result === 'string') {
  62. responseData = JSON.parse(response.result) as WeChatPrepayId;
  63. } else if (response.result instanceof Object) {
  64. responseData = response.result as WeChatPrepayId;
  65. } else {
  66. throw new Error('Unexpected response format');
  67. }
  68. const prepayId: string = responseData.prepay_id;
  69. return prepayId;
  70. } else {
  71. ToastUtil.showToast('微信下单失败');
  72. return '';
  73. }
  74. } catch (error) {
  75. ToastUtil.showToast('微信下单异常');
  76. return '';
  77. }
  78. }
  79. function constructSignatureString(appId: string, timestamp: string, nonceStr: string, prepayId: string): string {
  80. return `${appId}\n${timestamp}\n${nonceStr}\n${prepayId}\n`;
  81. }
  82. async function rsaSign2048(message: string): Promise<Uint8Array> {
  83. try {
  84. let keyGen = cryptoFramework.createAsyKeyGenerator("RSA2048")
  85. let key = keyGen.convertPemKeySync(null, CommonConstants.WX_PAY_RSA_PRIVATE_KEY)
  86. let sign = cryptoFramework.createSign('RSA2048|PKCS1|SHA256');
  87. let msgBlob: cryptoFramework.DataBlob = { data: StrUtil.strToUint8Array(message) }
  88. sign.initSync(key.priKey)
  89. let signature = await sign.sign(msgBlob)
  90. return signature.data
  91. } catch (error) {
  92. console.error(error, `onecold error code: ${error.code}`)
  93. return new Uint8Array()
  94. }
  95. }
  96. // 1. 工具函数:将 #RRGGBB 字符串和透明度转为 'rgba(r,g,b,a)' 字符串,深色模式下可返回深色变体
  97. function themeColorWithAlpha(themeColor: string, alpha: number, isDarkMode: boolean = false): string {
  98. if (isDarkMode) {
  99. // 深色模式下返回更深的灰色或半透明黑色
  100. return `rgba(34,34,34,${alpha + 0.2 > 1 ? 1 : alpha + 0.2})`;
  101. }
  102. const color = themeColor.replace('#', '');
  103. const r = parseInt(color.substring(0, 2), 16);
  104. const g = parseInt(color.substring(2, 4), 16);
  105. const b = parseInt(color.substring(4, 6), 16);
  106. return `rgba(${r},${g},${b},${alpha})`;
  107. }
  108. @Component
  109. // @Entry
  110. export struct UserCenter {
  111. @Consume isShowDrawer: boolean;
  112. @Consume offsetX: number;
  113. @State userName: string = '未登录用户';
  114. @State userId: number = 0;
  115. @State userAvatar: Resource = $r('app.media.icon_person2');
  116. @State userAvatarUrl: string = '';
  117. @State isVip: boolean = false;
  118. @State vipExpire: string = '';
  119. @State isLogin: boolean = false;
  120. @State hasActiveSubscription: boolean = false;
  121. @State subscriptionName: string = '';
  122. @State subscriptionEndDate: string = '';
  123. @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointTypeEnum.MD;
  124. @State vipFeatures: VipFeature[] = [
  125. {
  126. icon: $r('app.media.wusunyinzhi'),
  127. title: '无损音质',
  128. description: '专享无损音乐支持,让每个音符都完美呈现',
  129. isVipOnly: true,
  130. hasCome: true
  131. },
  132. // { icon: $r('app.media.lrc'), title: '歌词下载', description: '支持歌词API在线下载LRC歌词,实现完美同步', isVipOnly: true },
  133. // { icon: $r('app.media.cover'), title: '封面API', description: '支持设置自动下载封面', isVipOnly: true },
  134. {
  135. icon: $r('app.media.theme'),
  136. title: '主题定制',
  137. description: '自定义播放器主题,打造独一无二的个性风格',
  138. isVipOnly: true,
  139. hasCome: true
  140. },
  141. {
  142. icon: $r('app.media.no_ad'),
  143. title: '无广告',
  144. description: '清爽界面,无广告播放器',
  145. isVipOnly: true,
  146. hasCome: true
  147. },
  148. {
  149. icon: $r('app.media.skip'),
  150. title: '跳过头尾',
  151. description: '为某个歌单专门定制设置跳过头尾',
  152. isVipOnly: true,
  153. hasCome: true
  154. },
  155. {
  156. icon: $r('app.media.car'),
  157. title: 'HiCAR播放',
  158. description: '专为驾车体验优化的音乐播放模式',
  159. isVipOnly: true,
  160. hasCome: false
  161. },
  162. {
  163. icon: $r('app.media.music_wave'),
  164. title: '调音均衡器',
  165. description: '精准定制音效,带来极致听觉体验',
  166. isVipOnly: true,
  167. hasCome: false
  168. },
  169. ];
  170. @State vipPlans: VipPlanApi[] = [];
  171. @State selectedPayPlan: VipPlanApi | null = null;
  172. @State payType: number = 0; // 0: 微信, 1: 支付宝
  173. @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
  174. @State isDarkMode: boolean = false
  175. @State webUrl: string = CommonConstants.NEW_MEMBER_AGREEMENTS
  176. @State appName:string = ''
  177. @StorageProp('topRectHeight') topRectHeight: number = px2vp(AppUtil.getStatusBarHeight());
  178. @StorageProp('currentColorMode') @Watch('onColorModeChange') currentMode: number =
  179. ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
  180. @State showWxLogin: boolean = false;
  181. private wxApi = WXApi
  182. private wxEventHandler = WXEventHandler
  183. constructor() {
  184. super();
  185. }
  186. onColorModeChange() {
  187. this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
  188. }
  189. // 通过code换取用户信息
  190. async getUserInfoByCode(code: string) {
  191. await UserUtil.getUserInfoByCode(code);
  192. // 登录后同步本地会员到线上(仅本地有会员且线上无会员时)
  193. const expireDate = await Utility.getLocalNobleExpireDate();
  194. if (Utility.isNobleForOld() && !this.hasActiveSubscription && expireDate) {
  195. const success = await UserUtil.syncLocalNobleToServer(expireDate);
  196. if (success) {
  197. ToastUtil.showToast('本地会员已同步到账号');
  198. PreferencesUtil.putSync('isNoble', false);
  199. }
  200. }
  201. await this.fetchUserInfo();
  202. emitter.emit({ eventId: 1001 }, {})
  203. }
  204. // 通过华为账号信息换取用户信息和会员状态
  205. async getUserInfoByHuawei(openID: string, unionID: string, nickname: string, avatarUri: string) {
  206. await UserUtil.getUserInfoByHuawei(openID, unionID, nickname, avatarUri);
  207. // 登录后同步本地会员到线上(仅本地有会员且线上无会员时)
  208. const expireDate = await Utility.getLocalNobleExpireDate();
  209. if (Utility.isNobleForOld() && !this.hasActiveSubscription && expireDate) {
  210. const success = await UserUtil.syncLocalNobleToServer(expireDate);
  211. if (success) {
  212. ToastUtil.showToast('本地会员已同步到账号');
  213. }
  214. }
  215. await this.fetchUserInfo();
  216. emitter.emit({ eventId: 1001 }, {})
  217. }
  218. @Builder
  219. syncNobleDialogContentBuilder(): void {
  220. Text('检测到您是本地会员,为保障权益请登录账号,会员权益将自动同步到您的账号。')
  221. .fontSize(16)
  222. .fontColor('#222')
  223. .width('100%')
  224. .textAlign(TextAlign.Center)
  225. .padding(16)
  226. }
  227. aboutToAppear(): void {
  228. Utility.getAppName(getContext(this)).then((appName:string)=>{
  229. this.appName = appName
  230. })
  231. this.topRectHeight = px2vp(AppUtil.getStatusBarHeight());
  232. let themeColor = PreferencesUtil.getStringSync('THEME_COLOR', CommonConstants.DEFAULT_THEME_COLOR);
  233. this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
  234. AppStorage.setOrCreate('themeColor', themeColor);
  235. this.themeColor = themeColor;
  236. this.isLogin = PreferencesUtil.getBooleanSync('isLogin', false);
  237. this.userId = PreferencesUtil.getNumberSync('userId', 0);
  238. this.userName = PreferencesUtil.getStringSync('userName', '未登录用户');
  239. this.userAvatarUrl = PreferencesUtil.getStringSync('userAvatarUrl', '');
  240. this.subscriptionName = PreferencesUtil.getStringSync('subscriptionName', '');
  241. this.subscriptionEndDate = PreferencesUtil.getStringSync('subscriptionEndDate', '');
  242. this.hasActiveSubscription = PreferencesUtil.getBooleanSync('hasActiveSubscription', false);
  243. this.wxEventHandler.registerOnWXRespCallback(this.onWXResp)
  244. this.fetchVipPlans();
  245. if (this.isLogin) {
  246. void this.fetchUserInfo();
  247. }
  248. console.log('Heanup isLogin:' + this.isLogin)
  249. // 只在非同步跳转时弹窗提醒
  250. let params = router.getParams() as Record<string, Object>;
  251. let fromSyncNoble = params && params['fromSyncNoble'] === true;
  252. if (fromSyncNoble) {
  253. if (this.showWxLogin) {
  254. this.showLoginDialog();
  255. } else {
  256. this.huaweiQuickLogin();
  257. }
  258. } else if (!this.isLogin && Utility.isNobleForOld()) {
  259. DialogHelper.showCustomContentDialog({
  260. dialogId: 'syncNobleDialog',
  261. title: '会员同步提示',
  262. contentBuilder: () => {
  263. this.syncNobleDialogContentBuilder();
  264. },
  265. buttons: [
  266. { value: '暂不登录' },
  267. {
  268. value: '去登录',
  269. action: () => {
  270. if (this.showWxLogin) {
  271. this.showLoginDialog();
  272. } else {
  273. this.huaweiQuickLogin();
  274. }
  275. }
  276. }
  277. ]
  278. });
  279. }
  280. }
  281. showLoginDialog() {
  282. DialogHelper.showCustomContentDialog({
  283. dialogId: 'loginDialog',
  284. title: '',
  285. autoCancel: true,
  286. contentBuilder: () => {
  287. this.loginDialogContentBuilder();
  288. },
  289. buttons: []
  290. });
  291. }
  292. // 登录方式选择弹窗内容
  293. @Builder
  294. loginDialogContentBuilder(): void {
  295. Column() {
  296. Row() {
  297. // 微信登录图标按钮
  298. Column() {
  299. Button({ type: ButtonType.Normal, stateEffect: true }) {
  300. Column() {
  301. Image($r('app.media.wx_logo'))
  302. .width(56)
  303. .height(56)
  304. .margin({ bottom: 8 })
  305. Text('微信登录')
  306. .fontSize(15)
  307. .fontWeight(FontWeight.Bold)
  308. .fontColor('#333')
  309. }
  310. .alignItems(HorizontalAlign.Center)
  311. }
  312. .width(100)
  313. .height(100)
  314. // .visibility(PreferencesUtil.getBooleanSync('isShowWX',false)?Visibility.Visible:Visibility.None)
  315. .backgroundColor(Color.White)
  316. // .border({ color: this.themeColor, width: 2 })
  317. .borderRadius(20)
  318. .onClick(async () => {
  319. this.payType = 0;
  320. // 微信登录
  321. let req = new wxopensdk.SendAuthReq
  322. req.isOption1 = false
  323. req.nonAutomatic = true
  324. req.scope = 'snsapi_userinfo,snsapi_friend,snsapi_message,snsapi_contact'
  325. req.state = 'none'
  326. req.transaction = 'test123'
  327. let finished = await this.wxApi.sendReq(getContext(this) as common.UIAbilityContext, req)
  328. console.log('send request finished: ', finished)
  329. DialogHelper.closeDialog('loginDialog');
  330. })
  331. }
  332. .margin({ right: 24 })
  333. // 华为账号登录图标按钮
  334. Column() {
  335. Button({ type: ButtonType.Normal, stateEffect: true }) {
  336. Column() {
  337. Image($r('app.media.huawei'))
  338. .width(56)
  339. .height(56)
  340. .margin({ bottom: 8 })
  341. Text('华为登录')
  342. .fontSize(15)
  343. .fontWeight(FontWeight.Bold)
  344. .fontColor('#333')
  345. }
  346. .alignItems(HorizontalAlign.Center)
  347. }
  348. .width(100)
  349. .height(100)
  350. .backgroundColor(Color.White)
  351. // .border({ color: this.themeColor, width: 2 })
  352. .borderRadius(20)
  353. .onClick(() => {
  354. this.payType = 1;
  355. this.huaweiQuickLogin();
  356. DialogHelper.closeDialog('loginDialog');
  357. })
  358. }
  359. }
  360. .justifyContent(FlexAlign.Center)
  361. .width('100%')
  362. }
  363. .padding({
  364. left: 24,
  365. right: 24,
  366. top: 8,
  367. bottom: 24
  368. })
  369. .width('100%')
  370. }
  371. aboutToDisappear() {
  372. this.wxEventHandler.unregisterOnWXRespCallback(this.onWXResp)
  373. }
  374. build() {
  375. Column() {
  376. // 顶部安全区和自定义标题栏
  377. Column() {
  378. // 顶部安全区
  379. Blank()
  380. .height(this.topRectHeight)
  381. .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
  382. .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
  383. // 自定义标题栏(Stack实现绝对居中)
  384. Stack() {
  385. // 居中标题
  386. Text('用户中心')
  387. .fontSize(18)
  388. .fontColor(Color.White)
  389. .align(Alignment.Center)
  390. // 左右按钮
  391. Row() {
  392. Image($r('app.media.menu'))
  393. .width(26)
  394. .height(26)
  395. .margin({ left: 12, right: 8 })
  396. .onClick(() => {
  397. animateTo({ duration: 555 }, () => {
  398. // 动画闭包内控制Image组件的出现和消失
  399. this.isShowDrawer = !this.isShowDrawer
  400. this.offsetX = 0
  401. })
  402. })
  403. Blank().flexGrow(1)
  404. Blank().width(32)
  405. }
  406. .height(48)
  407. .width('100%')
  408. .alignItems(VerticalAlign.Center)
  409. }
  410. .height(48)
  411. .width('100%')
  412. .backgroundColor(this.isDarkMode ? $r('app.color.user_center_card_background') : this.themeColor)
  413. }
  414. Scroll() {
  415. Column() {
  416. this.buildUserInfoCard()
  417. if(!Utility.isForever()){
  418. this.buildVipPlans()
  419. }
  420. this.buildVipFeatures()
  421. // this.buildFunctionMenu()
  422. }
  423. .width('100%')
  424. }
  425. .scrollBar(BarState.Off)
  426. .edgeEffect(EdgeEffect.Spring)
  427. .layoutWeight(1)
  428. .padding({
  429. bottom: 45
  430. })
  431. .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
  432. .animation({ duration: 380, curve: Curve.Ease,delay:50 }))
  433. }
  434. .backgroundColor($r('app.color.index_background'))
  435. .width('100%')
  436. .height('100%')
  437. .layoutWeight(1)
  438. }
  439. // 用户信息卡片
  440. @Builder
  441. buildUserInfoCard() {
  442. Column() {
  443. Row() {
  444. Stack({ alignContent: Alignment.BottomEnd }){
  445. Image(this.userAvatarUrl && this.userAvatarUrl.length > 0 ? this.userAvatarUrl : this.userAvatar)
  446. .width(55)
  447. .height(55)
  448. .margin({ left: 12 })
  449. .borderRadius('50%')
  450. .clip(true)
  451. .backgroundColor(this.isDarkMode ? Color.Black : Color.White)
  452. Column() {
  453. Text('VIP')
  454. .fontSize(9)
  455. .padding(2)
  456. .textAlign(TextAlign.Center)
  457. .fontWeight(FontWeight.Bolder)
  458. .fontColor(Color.White)
  459. }
  460. .width(28)
  461. .height(16)
  462. .visibility(Utility.isNoble()?Visibility.Visible:Visibility.None)
  463. .borderRadius(15)
  464. .backgroundColor(this.themeColor)
  465. }
  466. Column() {
  467. Row() {
  468. Text(this.isLogin ? this.userName : '未登录用户')
  469. .fontSize(14)
  470. .fontWeight(FontWeight.Bold)
  471. .padding(5)
  472. .fontColor(this.isDarkMode ? Color.White : Color.Black)
  473. .textAlign(TextAlign.Start)
  474. .maxLines(1)
  475. .width(110)
  476. .margin({ right: 12 })
  477. }
  478. .justifyContent(FlexAlign.Start)
  479. .margin({ top: 2 })
  480. if (this.isLogin) {
  481. if (this.hasActiveSubscription) {
  482. Text(this.subscriptionName)
  483. .fontSize(13)
  484. .padding(5)
  485. .fontColor(themeColorWithAlpha(this.themeColor, 0.8, false))
  486. .textAlign(TextAlign.Start)
  487. Text('到期:' + this.subscriptionEndDate)
  488. .fontSize(11)
  489. .padding(5)
  490. .fontColor(this.isDarkMode ? Color.White : Color.Gray)
  491. .textAlign(TextAlign.Start)
  492. } else {
  493. Text('普通用户')
  494. .fontSize(13)
  495. .padding(5)
  496. .fontColor(this.isDarkMode ? Color.White : Color.Grey)
  497. .textAlign(TextAlign.Start)
  498. }
  499. }
  500. }
  501. .alignItems(HorizontalAlign.Start)
  502. .width(155)
  503. .margin({ left: 10 })
  504. Column() {
  505. if (this.isLogin) {
  506. Button('退出')
  507. .fontColor(Color.White)
  508. .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
  509. .height(36)
  510. .padding({ left:20,right:20,top:10,bottom:10 })
  511. .fontSize(13)
  512. .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
  513. .borderRadius(13)
  514. .margin({ left: 0 })
  515. .onClick(() => {
  516. this.isLogin = false
  517. this.userName = '未登录用户'
  518. this.userAvatar = $r('app.media.icon_person2')
  519. this.userAvatarUrl = ''
  520. this.isVip = false
  521. this.vipExpire = ''
  522. this.hasActiveSubscription = false;
  523. this.subscriptionName = '';
  524. this.subscriptionEndDate = '';
  525. PreferencesUtil.putSync('isLogin', false);
  526. PreferencesUtil.putSync('userId', 0);
  527. PreferencesUtil.putSync('userName', '未登录用户');
  528. PreferencesUtil.putSync('userAvatarUrl', '');
  529. PreferencesUtil.putSync('subscriptionName', '');
  530. PreferencesUtil.putSync('subscriptionEndDate', '');
  531. PreferencesUtil.putSync('hasActiveSubscription', false);
  532. PreferencesUtil.putSync('userToken', '');
  533. emitter.emit({ eventId: 1001 }, {})
  534. ToastUtil.showToast('已退出登录')
  535. })
  536. } else {
  537. Button('登录')
  538. .fontColor(Color.White)
  539. .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
  540. .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
  541. .height(36)
  542. .padding({ left:20,right:20,top:10,bottom:10 })
  543. .fontSize(13)
  544. .borderRadius(13)
  545. .margin({ right: 20 })
  546. }
  547. }
  548. .alignItems(HorizontalAlign.End)
  549. .margin({ left: 10, right: 10 })
  550. }
  551. .onClick(() => {
  552. if (!this.isLogin) {
  553. if (this.showWxLogin) {
  554. this.showLoginDialog();
  555. } else {
  556. this.huaweiQuickLogin();
  557. }
  558. }
  559. })
  560. .width('100%')
  561. .padding(10)
  562. }
  563. .backgroundColor($r('app.color.user_center_card_background'))
  564. .borderRadius(24)
  565. .margin({ left: 14, right: 14, top: 15 })
  566. .shadow({
  567. radius: 8,
  568. color: 0x11000000,
  569. offsetX: 0,
  570. offsetY: 2
  571. })
  572. }
  573. // 会员权益展示
  574. @Builder
  575. buildVipFeatures(): void {
  576. Column() {
  577. Text('会员权益')
  578. .fontSize(18)
  579. .fontWeight(FontWeight.Bold)
  580. .padding({ top: 24, bottom: 16 })
  581. .fontColor(this.isDarkMode ? Color.White : '#191919')
  582. .textAlign(TextAlign.Center);
  583. Grid() {
  584. ForEach(this.vipFeatures, (feature: VipFeature) => {
  585. GridItem() {
  586. Column() {
  587. Row() {
  588. Blank().layoutWeight(1)
  589. if (feature.hasCome === false) {
  590. Text('开发中')
  591. .fontSize(10)
  592. .fontColor(themeColorWithAlpha(this.themeColor, 0.9, false))
  593. .backgroundColor(themeColorWithAlpha(this.themeColor, 0.15, this.isDarkMode))
  594. .borderRadius(8)
  595. .padding({
  596. left: 6,
  597. right: 6,
  598. top: 2,
  599. bottom: 2
  600. })
  601. .margin({ top: 2, right: 2 })
  602. } else {
  603. Text('已开发')
  604. .fontSize(10)
  605. .fontColor(themeColorWithAlpha(this.themeColor, 0.9, false))
  606. .backgroundColor(themeColorWithAlpha(this.themeColor, 0.15, this.isDarkMode))
  607. .borderRadius(8)
  608. .padding({
  609. left: 6,
  610. right: 6,
  611. top: 2,
  612. bottom: 2
  613. })
  614. .margin({ top: 2, right: 2 })
  615. }
  616. }
  617. Row() {
  618. Blank().layoutWeight(1)
  619. Image(feature.icon)
  620. .width(32)
  621. .height(32)
  622. .fillColor(this.themeColor)
  623. Blank().layoutWeight(1)
  624. }
  625. .margin({ bottom: 8 })
  626. Text(feature.title)
  627. .fontSize(15)
  628. .fontWeight(FontWeight.Bold)
  629. .fontColor(this.isDarkMode ? Color.White : '#191919')
  630. .margin({ bottom: 2 })
  631. Text(feature.description)
  632. .fontSize(12)
  633. .fontColor(this.isDarkMode ? Color.Gray : '#888')
  634. .textAlign(TextAlign.Center)
  635. .maxLines(2)
  636. .margin({ bottom: 4 })
  637. if (feature.isVipOnly) {
  638. Text('VIP专享')
  639. .fontSize(10)
  640. .fontColor(this.isDarkMode ? Color.White : this.themeColor)
  641. .backgroundColor(this.isDarkMode ? $r('app.color.user_center_button_background') :
  642. themeColorWithAlpha(this.themeColor, 0.15, false))
  643. .borderRadius(8)
  644. .padding({
  645. left: 6,
  646. right: 6,
  647. top: 2,
  648. bottom: 2
  649. })
  650. .margin({ top: 2, bottom: 6 })
  651. }
  652. }
  653. .backgroundColor($r('app.color.user_center_card_background'))
  654. .borderRadius(16)
  655. .shadow({
  656. radius: 8,
  657. color: 0x11000000,
  658. offsetX: 0,
  659. offsetY: 2
  660. })
  661. .border({
  662. color: (this.isDarkMode ? $r('app.color.user_center_button_background') :
  663. themeColorWithAlpha(this.themeColor, 0.08, false))//未选中时的边框颜色
  664. , width: 2
  665. })
  666. .padding(12)
  667. .width('100%')
  668. .height(145)
  669. }
  670. })
  671. }
  672. .columnsTemplate('1fr 1fr')
  673. .columnsGap(16)
  674. .rowsGap(16)
  675. .margin({
  676. left: 16,
  677. right: 16,
  678. top: 0,
  679. bottom: 15
  680. })
  681. }
  682. .backgroundColor(this.isDarkMode ? Color.Black : Color.White)
  683. .borderRadius(24)
  684. .margin({
  685. left: 16,
  686. right: 16,
  687. top: 15,
  688. bottom: 15
  689. })
  690. }
  691. // 支付方式选择弹窗内容
  692. @Builder
  693. payDialogContentBuilder(): void {
  694. Column() {
  695. Row() {
  696. Image($r('app.media.wx_logo'))
  697. .width(22)
  698. .height(22)
  699. .alignSelf(ItemAlign.Center)
  700. .margin({ left: 25 })
  701. Text('微信支付')
  702. .margin({ left: 10, right: 20 })
  703. .fontSize(15)
  704. .fontColor(Color.Gray)
  705. .fontWeight(480)
  706. Blank()
  707. Checkbox()
  708. .select(this.payType === 0)
  709. .selectedColor(this.themeColor)
  710. .onChange((checked: boolean) => {
  711. if (checked) {
  712. this.payType = 0
  713. }
  714. })
  715. .shape(CheckBoxShape.CIRCLE)
  716. .margin({
  717. left: 20,
  718. top: 8,
  719. bottom: 8,
  720. right: 20
  721. })
  722. .width(22)
  723. .height(22)
  724. }
  725. .onClick(() => {
  726. this.payType = 0
  727. })
  728. .width('100%')
  729. .height(50)
  730. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  731. Row() {
  732. Image($r('app.media.alipay'))
  733. .width(22)
  734. .height(22)
  735. .alignSelf(ItemAlign.Center)
  736. .margin({ left: 25 })
  737. Text('支付宝')
  738. .margin({ left: 10, right: 20 })
  739. .fontSize(15)
  740. .fontColor(Color.Gray)
  741. .fontWeight(480)
  742. Blank()
  743. Checkbox()
  744. .select(this.payType === 1)
  745. .selectedColor(this.themeColor)
  746. .onChange((checked: boolean) => {
  747. if (checked) {
  748. this.payType = 1
  749. }
  750. })
  751. .shape(CheckBoxShape.CIRCLE)
  752. .margin({
  753. left: 20,
  754. top: 8,
  755. bottom: 8,
  756. right: 20
  757. })
  758. .width(22)
  759. .height(22)
  760. }
  761. .onClick(() => {
  762. this.payType = 1
  763. })
  764. .width('100%')
  765. .height(50)
  766. }
  767. .padding(24)
  768. .width('100%')
  769. }
  770. // 会员套餐选择
  771. @Builder
  772. buildVipPlans(): void {
  773. Column() {
  774. Text(this.subscriptionName=='永久会员'?'您还可以继续赞助支持我们':'会员套餐')
  775. .textAlign(TextAlign.Start)
  776. .padding({ left: 16, bottom: 15, top: 18 })
  777. .fontSize(18)
  778. .fontColor(this.isDarkMode ? Color.White : '#191919')
  779. .fontWeight(FontWeight.Bold)
  780. .fontFamily('鸿蒙黑体');
  781. // 横向滑动卡片区
  782. List() {
  783. ForEach(this.vipPlans, (plan: VipPlanApi, index: number) => {
  784. ListItem() {
  785. Column() {
  786. Text(this.subscriptionName=='永久会员'?"赞助套餐":plan.name)
  787. .fontSize(15)
  788. .fontWeight(FontWeight.Bold)
  789. .fontColor($r('app.color.user_center_font'))
  790. .margin({ top: 18, bottom: 8 })
  791. .textAlign(TextAlign.Center)
  792. // 价格上下排列
  793. Column() {
  794. Text('¥' + plan.price)
  795. .fontSize(24)
  796. .fontWeight(FontWeight.Bold)
  797. .fontColor(this.themeColor)
  798. .margin({ bottom: (plan.original_price && plan.discount_percent > 0) ? 2 : 0 })
  799. .textAlign(TextAlign.Center)
  800. if (plan.original_price && plan.discount_percent && plan.discount_percent > 0) {
  801. Text('¥' + plan.original_price)
  802. .fontSize(14)
  803. .fontColor(themeColorWithAlpha(this.themeColor, 0.7, false))
  804. .decoration({
  805. type: TextDecorationType.LineThrough,
  806. color: themeColorWithAlpha(this.themeColor, 0.7, false)
  807. })
  808. .fontStyle(FontStyle.Italic)
  809. .textAlign(TextAlign.Center)
  810. }
  811. }
  812. .alignItems(HorizontalAlign.Center)
  813. .margin({ bottom: 6 })
  814. Text(plan.duration === '0' ? '永久会员' : (plan.duration + '天会员'))
  815. .fontSize(13)
  816. .fontColor(this.isDarkMode ? Color.White : Color.Grey)
  817. .maxLines(1)
  818. .textAlign(TextAlign.Center)
  819. }
  820. .backgroundColor(this.selectedPayPlan && this.selectedPayPlan.id === plan.id ?
  821. themeColorWithAlpha(this.themeColor, 0.08, false) : (this.isDarkMode ? Color.Black : '#F7F7F7'))
  822. .borderRadius(18)
  823. .shadow({
  824. radius: 8,
  825. color: 0x11000000,
  826. offsetX: 0,
  827. offsetY: 2
  828. })
  829. .border({
  830. color: this.selectedPayPlan && this.selectedPayPlan.id === plan.id ?
  831. (this.isDarkMode ? Color.White : themeColorWithAlpha(this.themeColor, 0.3, this.isDarkMode))//选中时的边框颜色
  832. :
  833. (this.isDarkMode ? $r('app.color.user_center_button_background') :
  834. themeColorWithAlpha(this.themeColor, 0.08, this.isDarkMode))//未选中时的边框颜色
  835. , width: 2
  836. })
  837. .width(125)
  838. .height(125)
  839. .margin({ left: index === 0 ? 16 : 8, right: index === this.vipPlans.length - 1 ? 16 : 8 })
  840. .onClick(() => {
  841. this.selectedPayPlan = plan;
  842. })
  843. .alignItems(HorizontalAlign.Center)
  844. }
  845. })
  846. }
  847. .listDirection(Axis.Horizontal)
  848. .height(130)
  849. .edgeEffect(EdgeEffect.None)
  850. .scrollBar(BarState.Off)
  851. .margin({ bottom: 18 })
  852. Button(this.isVip ? '升级会员' : '立即开通')
  853. .borderRadius(24)
  854. .backgroundColor(this.isDarkMode ? $r('app.color.user_center_button_background') : this.themeColor)
  855. .width(200)
  856. .fontColor(Color.White)
  857. .fontSize(18)
  858. .padding(12)
  859. .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
  860. .fontWeight(FontWeight.Bold)
  861. .margin({ top: 1, bottom: 15 })
  862. .alignSelf(ItemAlign.Center)
  863. .onClick(() => {
  864. if (!this.isLogin) {
  865. ToastUtil.showToast('请先登录');
  866. return;
  867. }
  868. if (!this.selectedPayPlan) {
  869. ToastUtil.showToast('请选择套餐');
  870. return;
  871. }
  872. DialogHelper.showCustomContentDialog({
  873. dialogId: 'payDialog',
  874. title: '请选择支付方式',
  875. autoCancel: true,
  876. contentBuilder: () => {
  877. this.payDialogContentBuilder();
  878. },
  879. buttons: [
  880. { value: '取消' },
  881. {
  882. value: '确认支付',
  883. action: () => {
  884. if (this.selectedPayPlan) {
  885. void this.doPay(this.selectedPayPlan);
  886. }
  887. DialogHelper.closeDialog('payDialog');
  888. }
  889. }
  890. ]
  891. });
  892. });
  893. Row() {
  894. Text('购买前请阅读')
  895. .fontSize(12)
  896. .fontColor(this.isDarkMode ? Color.White : '#888')
  897. Text('《会员协议》')
  898. .fontSize(12)
  899. .fontColor(this.isDarkMode ? Color.Blue : this.themeColor)
  900. .onClick(() => {
  901. router.pushUrl({
  902. url: 'pages/WebIndex',
  903. params: { titleName: '会员协议', webUrl: this.webUrl }
  904. });
  905. // router.pushUrl({ url: this.webUrl });
  906. });
  907. }
  908. .justifyContent(FlexAlign.Center)
  909. .width('100%')
  910. .padding({ bottom: 16 });
  911. }
  912. .margin({ left: 14, right: 14, top: 15 })
  913. .backgroundColor($r('app.color.user_center_card_background'))
  914. .borderRadius(24)
  915. }
  916. // 功能菜单
  917. @Builder
  918. buildFunctionMenu(): void {
  919. Column() {
  920. this.menuItem($r('app.media.icon_favorite'), '我的收藏', () => {
  921. if (!this.isLogin) {
  922. ToastUtil.showToast('请先登录')
  923. return
  924. }
  925. // 跳转到收藏页面
  926. ToastUtil.showToast('敬请期待')
  927. })
  928. Line().width('90%').height(0.5).backgroundColor(0xFFEFEFEF)
  929. this.menuItem($r('app.media.icon_history'), '播放历史', () => {
  930. if (!this.isLogin) {
  931. ToastUtil.showToast('请先登录')
  932. return
  933. }
  934. // 跳转到历史记录页面
  935. ToastUtil.showToast('敬请期待')
  936. })
  937. Line().width('90%').height(0.5).backgroundColor(0xFFEFEFEF)
  938. this.menuItem($r('app.media.setting_red'), '设置', () => {
  939. // 跳转到设置页面
  940. router.pushUrl({
  941. url: 'pages/SettingPage'
  942. });
  943. })
  944. }
  945. .backgroundColor(Color.White)
  946. .borderRadius(24)
  947. .margin({ left: 20, right: 20, top: 24 })
  948. }
  949. // 华为一键登录
  950. async huaweiQuickLogin() {
  951. const logTag = 'UserCenter';
  952. const domainId = 0x0000;
  953. try {
  954. const authRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();
  955. authRequest.scopes = ['profile'];
  956. authRequest.permissions = ['idtoken'];
  957. authRequest.forceAuthorization = true;
  958. authRequest.state = util.generateRandomUUID();
  959. authRequest.idTokenSignAlgorithm = authentication.IdTokenSignAlgorithm.PS256;
  960. const controller = new authentication.AuthenticationController(getContext(this));
  961. const response: authentication.AuthorizationWithHuaweiIDResponse = await controller.executeRequest(authRequest);
  962. const credential = response.data;
  963. const nickName = credential?.nickName ?? '华为用户';
  964. const avatarUri = credential?.avatarUri ?? '';
  965. const openID = credential?.openID;
  966. const unionID = credential?.unionID ?? '';
  967. // hilog.info(domainId, logTag, `华为登录成功,昵称: ${nickName}, openID: ${openID}`);
  968. // 调用接口获取用户信息和会员状态
  969. if (openID) {
  970. await this.getUserInfoByHuawei(openID, unionID, nickName, avatarUri);
  971. } else {
  972. ToastUtil.showToast('openID获取失败');
  973. }
  974. } catch (error) {
  975. hilog.error(domainId, logTag, `华为登录失败: ${error.code}, ${error.message}`);
  976. this.dealHuaweiLoginError(error as BusinessError);
  977. ToastUtil.showToast('华为登录失败');
  978. }
  979. }
  980. dealHuaweiLoginError(error: BusinessError): void {
  981. if (error.code === 1001) {
  982. ToastUtil.showToast('未登录华为账号');
  983. } else if (error.code === 1002) {
  984. ToastUtil.showToast('网络异常');
  985. } else if (error.code === 1003) {
  986. ToastUtil.showToast('内部错误');
  987. } else if (error.code === 1004) {
  988. ToastUtil.showToast('用户取消授权');
  989. } else if (error.code === 1005) {
  990. ToastUtil.showToast('系统服务异常');
  991. } else if (error.code === 1006) {
  992. ToastUtil.showToast('请求被拒绝');
  993. } else if (error.code === 1007) {
  994. ToastUtil.showToast('无权限');
  995. } else {
  996. ToastUtil.showToast('登录失败');
  997. }
  998. }
  999. // 菜单项构建
  1000. @Builder
  1001. menuItem(icon: Resource, text: string, onClick: () => void): void {
  1002. Row() {
  1003. Image(icon)
  1004. .width(22)
  1005. .height(22)
  1006. .alignSelf(ItemAlign.Center)
  1007. .margin({ left: 22 })
  1008. Text(text)
  1009. .margin({ left: 10, right: 20 })
  1010. .fontSize(15)
  1011. .fontColor(Color.Gray)
  1012. .fontWeight(480)
  1013. Blank()
  1014. Image($r('app.media.arrow_right'))
  1015. .width(22)
  1016. .height(22)
  1017. .margin({ left: 20, right: 20 })
  1018. .align(Alignment.Center)
  1019. }
  1020. .width('100%')
  1021. .height(50)
  1022. .onClick(onClick)
  1023. }
  1024. async fetchVipPlans() {
  1025. try {
  1026. const url = 'https://pay.ss5.xyz/subscription/get_list';
  1027. const httpRequest = http.createHttp();
  1028. const options: http.HttpRequestOptions = {
  1029. method: http.RequestMethod.GET,
  1030. readTimeout: 3000,
  1031. connectTimeout: 3000,
  1032. };
  1033. const response: http.HttpResponse = await httpRequest.request(url, options);
  1034. if (response.responseCode === 200) {
  1035. let res = response.result as string;
  1036. let json: VipPlanListApiResponse = JSON.parse(res) as VipPlanListApiResponse;
  1037. if (json.code === 0 && json.data && json.data.plans) {
  1038. this.vipPlans = json.data.plans;
  1039. this.showWxLogin = !!json.data.show_wx_login;
  1040. if (this.vipPlans.length > 0) {
  1041. this.selectedPayPlan = this.vipPlans[0]; // 默认选中第一个套餐
  1042. }
  1043. } else {
  1044. ToastUtil.showToast('套餐数据解析失败');
  1045. }
  1046. } else {
  1047. ToastUtil.showToast('获取套餐失败');
  1048. }
  1049. } catch (e) {
  1050. ToastUtil.showToast('获取套餐异常');
  1051. }
  1052. }
  1053. async doPay(plan: VipPlanApi) {
  1054. if (this.payType === 0) {
  1055. await this.doWxPay(plan);
  1056. } else if (this.payType === 1) {
  1057. await this.doAliPay(plan);
  1058. }
  1059. }
  1060. async doWxPay(plan: VipPlanApi) {
  1061. if (!(await isHasWX())) {
  1062. ToastUtil.showToast('没有安装微信');
  1063. return;
  1064. }
  1065. const appName = PreferencesUtil.getStringSync('appName', 'TTMusic');
  1066. hilog.info(0x0000, 'UserCenter', `doWxPay: current this.userId is ${this.userId}`);
  1067. const prepayId = await createWeChatOrder(CommonConstants.WX_PAY_APPID, CommonConstants.WX_PAY_MCHID,
  1068. appName, Math.round(Number(plan.price) * 100), pinyin4js.getShortPinyin(this.appName), this.userId, plan.id);
  1069. if (prepayId) {
  1070. await this.wechatPay(prepayId);
  1071. } else {
  1072. ToastUtil.showToast('微信下单失败');
  1073. }
  1074. }
  1075. async wechatPay(prepayId: string) {
  1076. let nonceStr = RandomUtil.getRandomStr(16, 'abcdefghijklmnopqrstuvwxyz');
  1077. const timestamp: number = Math.floor(Date.now() / 1000);
  1078. const signString = constructSignatureString(CommonConstants.WX_PAY_APPID, timestamp + '', nonceStr, prepayId);
  1079. let signStr = await rsaSign2048(signString);
  1080. let signStrBase64 = Base64Util.encodeToStrSync(signStr);
  1081. let req = new PayReq();
  1082. req.partnerId = CommonConstants.WX_PAY_MCHID;
  1083. req.appId = CommonConstants.WX_PAY_APPID;
  1084. req.packageValue = 'Sign=WXPay';
  1085. req.prepayId = prepayId;
  1086. req.nonceStr = nonceStr;
  1087. req.timeStamp = timestamp + '';
  1088. req.sign = signStrBase64;
  1089. req.extData = 'extData';
  1090. let finished = await this.wxApi.sendReq(getContext(this) as common.UIAbilityContext, req);
  1091. hilog.info(0x0000, 'UserCenter', 'send request finished: ', finished);
  1092. }
  1093. async doAliPay(plan: VipPlanApi) {
  1094. const appName = PreferencesUtil.getStringSync('appName', 'TTMusic');
  1095. OrderInfoUtil.getOrderInfo(Number(plan.price), appName, this.userId, plan.id).then((orderInfo) => {
  1096. console.log('Heanup: alipay orderInfo = ' + json.stringify(orderInfo))
  1097. new Pay().pay(orderInfo, true).then(async (result) => {
  1098. if (result.get('resultStatus') === '9000') {
  1099. ToastUtil.showToast('支付成功!');
  1100. void this.fetchUserInfo();
  1101. // 可在此处刷新会员状态
  1102. } else {
  1103. ToastUtil.showToast('取消支付!');
  1104. }
  1105. }).catch((error: BusinessError) => {
  1106. ToastUtil.showToast('支付失败!' + error.message);
  1107. });
  1108. });
  1109. }
  1110. // 每次进入页面时刷新用户数据
  1111. async fetchUserInfo() {
  1112. // console.log('Heanup 刷新用户信息:'+token)
  1113. await UserUtil.fetchUserInfo();
  1114. // console.log("Heanup UserInfo:" + json.stringify(user_info))
  1115. this.isLogin = PreferencesUtil.getBooleanSync('isLogin', false);
  1116. this.userId = PreferencesUtil.getNumberSync('userId', 0);
  1117. this.userName = PreferencesUtil.getStringSync('userName', '未登录用户');
  1118. this.userAvatarUrl = PreferencesUtil.getStringSync('userAvatarUrl', '');
  1119. this.subscriptionName = PreferencesUtil.getStringSync('subscriptionName', '');
  1120. this.subscriptionEndDate = PreferencesUtil.getStringSync('subscriptionEndDate', '');
  1121. this.hasActiveSubscription = PreferencesUtil.getBooleanSync('hasActiveSubscription', false);
  1122. // console.log('Heanup hasActiveSubscription:' + this.hasActiveSubscription)
  1123. // 新增:同步线上会员状态到本地,便于 Utility.isNoble 全局判断
  1124. PreferencesUtil.putSync('hasActiveSubscription', this.hasActiveSubscription);
  1125. }
  1126. // 微信回调处理
  1127. private onWXResp: OnWXResp = (resp): void => {
  1128. if (resp.errCode === ErrCode.ERR_OK) {
  1129. // 判断回调类型
  1130. if (resp.type === wxopensdk.Command.kCommandSendAuth) {
  1131. // 微信登录成功回调
  1132. const authResp = resp as wxopensdk.SendAuthResp; // 转换为授权响应类型,以便访问code
  1133. this.getUserInfoByCode(resp?.['code'])
  1134. DialogHelper.closeDialog('loginDialog');
  1135. ToastUtil.showToast('登录成功!');
  1136. } else if (resp.type === wxopensdk.Command.kCommandPay) {
  1137. // 微信支付成功回调
  1138. ToastUtil.showToast('支付成功!');
  1139. // 可在此处刷新会员状态或其他支付成功后的业务逻辑
  1140. // this.fetchVipPlans(); // 刷新会员套餐信息
  1141. this.fetchUserInfo();
  1142. } else {
  1143. // 其他类型的成功回调
  1144. ToastUtil.showToast('操作成功!');
  1145. }
  1146. } else {
  1147. // 处理错误情况
  1148. if (resp.type === wxopensdk.Command.kCommandSendAuth) {
  1149. ToastUtil.showToast('微信登录失败!');
  1150. } else if (resp.type === wxopensdk.Command.kCommandPay) {
  1151. ToastUtil.showToast('支付失败!');
  1152. } else {
  1153. ToastUtil.showToast('操作失败!');
  1154. }
  1155. }
  1156. }
  1157. }