UserCenter.ets 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217
  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. PreferencesUtil.putSync('isForever', false);
  534. emitter.emit({ eventId: 1001 }, {})
  535. ToastUtil.showToast('已退出登录')
  536. })
  537. } else {
  538. Button('登录')
  539. .fontColor(Color.White)
  540. .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
  541. .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
  542. .height(36)
  543. .padding({ left:20,right:20,top:10,bottom:10 })
  544. .fontSize(13)
  545. .borderRadius(13)
  546. .margin({ right: 20 })
  547. }
  548. }
  549. .alignItems(HorizontalAlign.End)
  550. .margin({ left: 10, right: 10 })
  551. }
  552. .onClick(() => {
  553. if (!this.isLogin) {
  554. if (this.showWxLogin) {
  555. this.showLoginDialog();
  556. } else {
  557. this.huaweiQuickLogin();
  558. }
  559. }
  560. })
  561. .width('100%')
  562. .padding(10)
  563. }
  564. .backgroundColor($r('app.color.user_center_card_background'))
  565. .borderRadius(24)
  566. .margin({ left: 14, right: 14, top: 15 })
  567. .shadow({
  568. radius: 8,
  569. color: 0x11000000,
  570. offsetX: 0,
  571. offsetY: 2
  572. })
  573. }
  574. // 会员权益展示
  575. @Builder
  576. buildVipFeatures(): void {
  577. Column() {
  578. Text('会员权益')
  579. .fontSize(18)
  580. .fontWeight(FontWeight.Bold)
  581. .padding({ top: 24, bottom: 16 })
  582. .fontColor(this.isDarkMode ? Color.White : '#191919')
  583. .textAlign(TextAlign.Center);
  584. Grid() {
  585. ForEach(this.vipFeatures, (feature: VipFeature) => {
  586. GridItem() {
  587. Column() {
  588. Row() {
  589. Blank().layoutWeight(1)
  590. if (feature.hasCome === false) {
  591. Text('开发中')
  592. .fontSize(10)
  593. .fontColor(themeColorWithAlpha(this.themeColor, 0.9, false))
  594. .backgroundColor(themeColorWithAlpha(this.themeColor, 0.15, this.isDarkMode))
  595. .borderRadius(8)
  596. .padding({
  597. left: 6,
  598. right: 6,
  599. top: 2,
  600. bottom: 2
  601. })
  602. .margin({ top: 2, right: 2 })
  603. } else {
  604. Text('已开发')
  605. .fontSize(10)
  606. .fontColor(themeColorWithAlpha(this.themeColor, 0.9, false))
  607. .backgroundColor(themeColorWithAlpha(this.themeColor, 0.15, this.isDarkMode))
  608. .borderRadius(8)
  609. .padding({
  610. left: 6,
  611. right: 6,
  612. top: 2,
  613. bottom: 2
  614. })
  615. .margin({ top: 2, right: 2 })
  616. }
  617. }
  618. Row() {
  619. Blank().layoutWeight(1)
  620. Image(feature.icon)
  621. .width(32)
  622. .height(32)
  623. .fillColor(this.themeColor)
  624. Blank().layoutWeight(1)
  625. }
  626. .margin({ bottom: 8 })
  627. Text(feature.title)
  628. .fontSize(15)
  629. .fontWeight(FontWeight.Bold)
  630. .fontColor(this.isDarkMode ? Color.White : '#191919')
  631. .margin({ bottom: 2 })
  632. Text(feature.description)
  633. .fontSize(12)
  634. .fontColor(this.isDarkMode ? Color.Gray : '#888')
  635. .textAlign(TextAlign.Center)
  636. .maxLines(2)
  637. .margin({ bottom: 4 })
  638. if (feature.isVipOnly) {
  639. Text('VIP专享')
  640. .fontSize(10)
  641. .fontColor(this.isDarkMode ? Color.White : this.themeColor)
  642. .backgroundColor(this.isDarkMode ? $r('app.color.user_center_button_background') :
  643. themeColorWithAlpha(this.themeColor, 0.15, false))
  644. .borderRadius(8)
  645. .padding({
  646. left: 6,
  647. right: 6,
  648. top: 2,
  649. bottom: 2
  650. })
  651. .margin({ top: 2, bottom: 6 })
  652. }
  653. }
  654. .backgroundColor($r('app.color.user_center_card_background'))
  655. .borderRadius(16)
  656. .shadow({
  657. radius: 8,
  658. color: 0x11000000,
  659. offsetX: 0,
  660. offsetY: 2
  661. })
  662. .border({
  663. color: (this.isDarkMode ? $r('app.color.user_center_button_background') :
  664. themeColorWithAlpha(this.themeColor, 0.08, false))//未选中时的边框颜色
  665. , width: 2
  666. })
  667. .padding(12)
  668. .width('100%')
  669. .height(145)
  670. }
  671. })
  672. }
  673. .columnsTemplate('1fr 1fr')
  674. .columnsGap(16)
  675. .rowsGap(16)
  676. .margin({
  677. left: 16,
  678. right: 16,
  679. top: 0,
  680. bottom: 15
  681. })
  682. }
  683. .backgroundColor(this.isDarkMode ? Color.Black : Color.White)
  684. .borderRadius(24)
  685. .margin({
  686. left: 16,
  687. right: 16,
  688. top: 15,
  689. bottom: 15
  690. })
  691. }
  692. // 支付方式选择弹窗内容
  693. @Builder
  694. payDialogContentBuilder(): void {
  695. Column() {
  696. Row() {
  697. Image($r('app.media.wx_logo'))
  698. .width(22)
  699. .height(22)
  700. .alignSelf(ItemAlign.Center)
  701. .margin({ left: 25 })
  702. Text('微信支付')
  703. .margin({ left: 10, right: 20 })
  704. .fontSize(15)
  705. .fontColor(Color.Gray)
  706. .fontWeight(480)
  707. Blank()
  708. Checkbox()
  709. .select(this.payType === 0)
  710. .selectedColor(this.themeColor)
  711. .onChange((checked: boolean) => {
  712. if (checked) {
  713. this.payType = 0
  714. }
  715. })
  716. .shape(CheckBoxShape.CIRCLE)
  717. .margin({
  718. left: 20,
  719. top: 8,
  720. bottom: 8,
  721. right: 20
  722. })
  723. .width(22)
  724. .height(22)
  725. }
  726. .onClick(() => {
  727. this.payType = 0
  728. })
  729. .width('100%')
  730. .height(50)
  731. Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
  732. Row() {
  733. Image($r('app.media.alipay'))
  734. .width(22)
  735. .height(22)
  736. .alignSelf(ItemAlign.Center)
  737. .margin({ left: 25 })
  738. Text('支付宝')
  739. .margin({ left: 10, right: 20 })
  740. .fontSize(15)
  741. .fontColor(Color.Gray)
  742. .fontWeight(480)
  743. Blank()
  744. Checkbox()
  745. .select(this.payType === 1)
  746. .selectedColor(this.themeColor)
  747. .onChange((checked: boolean) => {
  748. if (checked) {
  749. this.payType = 1
  750. }
  751. })
  752. .shape(CheckBoxShape.CIRCLE)
  753. .margin({
  754. left: 20,
  755. top: 8,
  756. bottom: 8,
  757. right: 20
  758. })
  759. .width(22)
  760. .height(22)
  761. }
  762. .onClick(() => {
  763. this.payType = 1
  764. })
  765. .width('100%')
  766. .height(50)
  767. }
  768. .padding(24)
  769. .width('100%')
  770. }
  771. // 会员套餐选择
  772. @Builder
  773. buildVipPlans(): void {
  774. Column() {
  775. Text(this.subscriptionName=='永久会员'?'您还可以继续赞助支持我们':'会员套餐')
  776. .textAlign(TextAlign.Start)
  777. .padding({ left: 16, bottom: 15, top: 18 })
  778. .fontSize(18)
  779. .fontColor(this.isDarkMode ? Color.White : '#191919')
  780. .fontWeight(FontWeight.Bold)
  781. .fontFamily('鸿蒙黑体');
  782. // 横向滑动卡片区
  783. List() {
  784. ForEach(this.vipPlans, (plan: VipPlanApi, index: number) => {
  785. ListItem() {
  786. Column() {
  787. Text(this.subscriptionName=='永久会员'?"赞助套餐":plan.name)
  788. .fontSize(15)
  789. .fontWeight(FontWeight.Bold)
  790. .fontColor($r('app.color.user_center_font'))
  791. .margin({ top: 18, bottom: 8 })
  792. .textAlign(TextAlign.Center)
  793. // 价格上下排列
  794. Column() {
  795. Text('¥' + plan.price)
  796. .fontSize(24)
  797. .fontWeight(FontWeight.Bold)
  798. .fontColor(this.themeColor)
  799. .margin({ bottom: (plan.original_price && plan.discount_percent > 0) ? 2 : 0 })
  800. .textAlign(TextAlign.Center)
  801. if (plan.original_price && plan.discount_percent && plan.discount_percent > 0) {
  802. Text('¥' + plan.original_price)
  803. .fontSize(14)
  804. .fontColor(themeColorWithAlpha(this.themeColor, 0.7, false))
  805. .decoration({
  806. type: TextDecorationType.LineThrough,
  807. color: themeColorWithAlpha(this.themeColor, 0.7, false)
  808. })
  809. .fontStyle(FontStyle.Italic)
  810. .textAlign(TextAlign.Center)
  811. }
  812. }
  813. .alignItems(HorizontalAlign.Center)
  814. .margin({ bottom: 6 })
  815. Text(plan.duration === '0' ? '永久会员' : (plan.duration + '天会员'))
  816. .fontSize(13)
  817. .fontColor(this.isDarkMode ? Color.White : Color.Grey)
  818. .maxLines(1)
  819. .textAlign(TextAlign.Center)
  820. }
  821. .backgroundColor(this.selectedPayPlan && this.selectedPayPlan.id === plan.id ?
  822. themeColorWithAlpha(this.themeColor, 0.08, false) : (this.isDarkMode ? Color.Black : '#F7F7F7'))
  823. .borderRadius(18)
  824. .shadow({
  825. radius: 8,
  826. color: 0x11000000,
  827. offsetX: 0,
  828. offsetY: 2
  829. })
  830. .border({
  831. color: this.selectedPayPlan && this.selectedPayPlan.id === plan.id ?
  832. (this.isDarkMode ? Color.White : themeColorWithAlpha(this.themeColor, 0.3, this.isDarkMode))//选中时的边框颜色
  833. :
  834. (this.isDarkMode ? $r('app.color.user_center_button_background') :
  835. themeColorWithAlpha(this.themeColor, 0.08, this.isDarkMode))//未选中时的边框颜色
  836. , width: 2
  837. })
  838. .width(125)
  839. .height(125)
  840. .margin({ left: index === 0 ? 16 : 8, right: index === this.vipPlans.length - 1 ? 16 : 8 })
  841. .onClick(() => {
  842. this.selectedPayPlan = plan;
  843. })
  844. .alignItems(HorizontalAlign.Center)
  845. }
  846. })
  847. }
  848. .listDirection(Axis.Horizontal)
  849. .height(130)
  850. .edgeEffect(EdgeEffect.None)
  851. .scrollBar(BarState.Off)
  852. .margin({ bottom: 18 })
  853. Button(this.isVip ? '升级会员' : '立即开通')
  854. .borderRadius(24)
  855. .backgroundColor(this.isDarkMode ? $r('app.color.user_center_button_background') : this.themeColor)
  856. .width(200)
  857. .fontColor(Color.White)
  858. .fontSize(18)
  859. .padding(12)
  860. .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
  861. .fontWeight(FontWeight.Bold)
  862. .margin({ top: 1, bottom: 15 })
  863. .alignSelf(ItemAlign.Center)
  864. .onClick(() => {
  865. if (!this.isLogin) {
  866. ToastUtil.showToast('请先登录');
  867. return;
  868. }
  869. if (!this.selectedPayPlan) {
  870. ToastUtil.showToast('请选择套餐');
  871. return;
  872. }
  873. DialogHelper.showCustomContentDialog({
  874. dialogId: 'payDialog',
  875. title: '请选择支付方式',
  876. autoCancel: true,
  877. contentBuilder: () => {
  878. this.payDialogContentBuilder();
  879. },
  880. buttons: [
  881. { value: '取消' },
  882. {
  883. value: '确认支付',
  884. action: () => {
  885. if (this.selectedPayPlan) {
  886. void this.doPay(this.selectedPayPlan);
  887. }
  888. DialogHelper.closeDialog('payDialog');
  889. }
  890. }
  891. ]
  892. });
  893. });
  894. Row() {
  895. Text('购买前请阅读')
  896. .fontSize(12)
  897. .fontColor(this.isDarkMode ? Color.White : '#888')
  898. Text('《会员协议》')
  899. .fontSize(12)
  900. .fontColor(this.isDarkMode ? Color.Blue : this.themeColor)
  901. .onClick(() => {
  902. router.pushUrl({
  903. url: 'pages/WebIndex',
  904. params: { titleName: '会员协议', webUrl: this.webUrl }
  905. });
  906. // router.pushUrl({ url: this.webUrl });
  907. });
  908. }
  909. .justifyContent(FlexAlign.Center)
  910. .width('100%')
  911. .padding({ bottom: 16 });
  912. }
  913. .margin({ left: 14, right: 14, top: 15 })
  914. .backgroundColor($r('app.color.user_center_card_background'))
  915. .borderRadius(24)
  916. }
  917. // 功能菜单
  918. @Builder
  919. buildFunctionMenu(): void {
  920. Column() {
  921. this.menuItem($r('app.media.icon_favorite'), '我的收藏', () => {
  922. if (!this.isLogin) {
  923. ToastUtil.showToast('请先登录')
  924. return
  925. }
  926. // 跳转到收藏页面
  927. ToastUtil.showToast('敬请期待')
  928. })
  929. Line().width('90%').height(0.5).backgroundColor(0xFFEFEFEF)
  930. this.menuItem($r('app.media.icon_history'), '播放历史', () => {
  931. if (!this.isLogin) {
  932. ToastUtil.showToast('请先登录')
  933. return
  934. }
  935. // 跳转到历史记录页面
  936. ToastUtil.showToast('敬请期待')
  937. })
  938. Line().width('90%').height(0.5).backgroundColor(0xFFEFEFEF)
  939. this.menuItem($r('app.media.setting_red'), '设置', () => {
  940. // 跳转到设置页面
  941. router.pushUrl({
  942. url: 'pages/SettingPage'
  943. });
  944. })
  945. }
  946. .backgroundColor(Color.White)
  947. .borderRadius(24)
  948. .margin({ left: 20, right: 20, top: 24 })
  949. }
  950. // 华为一键登录
  951. async huaweiQuickLogin() {
  952. const logTag = 'UserCenter';
  953. const domainId = 0x0000;
  954. try {
  955. const authRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();
  956. authRequest.scopes = ['profile'];
  957. authRequest.permissions = ['idtoken'];
  958. authRequest.forceAuthorization = true;
  959. authRequest.state = util.generateRandomUUID();
  960. authRequest.idTokenSignAlgorithm = authentication.IdTokenSignAlgorithm.PS256;
  961. const controller = new authentication.AuthenticationController(getContext(this));
  962. const response: authentication.AuthorizationWithHuaweiIDResponse = await controller.executeRequest(authRequest);
  963. const credential = response.data;
  964. const nickName = credential?.nickName ?? '华为用户';
  965. const avatarUri = credential?.avatarUri ?? '';
  966. const openID = credential?.openID;
  967. const unionID = credential?.unionID ?? '';
  968. // hilog.info(domainId, logTag, `华为登录成功,昵称: ${nickName}, openID: ${openID}`);
  969. // 调用接口获取用户信息和会员状态
  970. if (openID) {
  971. await this.getUserInfoByHuawei(openID, unionID, nickName, avatarUri);
  972. } else {
  973. ToastUtil.showToast('openID获取失败');
  974. }
  975. } catch (error) {
  976. hilog.error(domainId, logTag, `华为登录失败: ${error.code}, ${error.message}`);
  977. this.dealHuaweiLoginError(error as BusinessError);
  978. ToastUtil.showToast('华为登录失败');
  979. }
  980. }
  981. dealHuaweiLoginError(error: BusinessError): void {
  982. if (error.code === 1001) {
  983. ToastUtil.showToast('未登录华为账号');
  984. } else if (error.code === 1002) {
  985. ToastUtil.showToast('网络异常');
  986. } else if (error.code === 1003) {
  987. ToastUtil.showToast('内部错误');
  988. } else if (error.code === 1004) {
  989. ToastUtil.showToast('用户取消授权');
  990. } else if (error.code === 1005) {
  991. ToastUtil.showToast('系统服务异常');
  992. } else if (error.code === 1006) {
  993. ToastUtil.showToast('请求被拒绝');
  994. } else if (error.code === 1007) {
  995. ToastUtil.showToast('无权限');
  996. } else {
  997. ToastUtil.showToast('登录失败');
  998. }
  999. }
  1000. // 菜单项构建
  1001. @Builder
  1002. menuItem(icon: Resource, text: string, onClick: () => void): void {
  1003. Row() {
  1004. Image(icon)
  1005. .width(22)
  1006. .height(22)
  1007. .alignSelf(ItemAlign.Center)
  1008. .margin({ left: 22 })
  1009. Text(text)
  1010. .margin({ left: 10, right: 20 })
  1011. .fontSize(15)
  1012. .fontColor(Color.Gray)
  1013. .fontWeight(480)
  1014. Blank()
  1015. Image($r('app.media.arrow_right'))
  1016. .width(22)
  1017. .height(22)
  1018. .margin({ left: 20, right: 20 })
  1019. .align(Alignment.Center)
  1020. }
  1021. .width('100%')
  1022. .height(50)
  1023. .onClick(onClick)
  1024. }
  1025. async fetchVipPlans() {
  1026. try {
  1027. const url = 'https://pay.ss5.xyz/subscription/get_list';
  1028. const httpRequest = http.createHttp();
  1029. const options: http.HttpRequestOptions = {
  1030. method: http.RequestMethod.GET,
  1031. readTimeout: 3000,
  1032. connectTimeout: 3000,
  1033. };
  1034. const response: http.HttpResponse = await httpRequest.request(url, options);
  1035. if (response.responseCode === 200) {
  1036. let res = response.result as string;
  1037. let json: VipPlanListApiResponse = JSON.parse(res) as VipPlanListApiResponse;
  1038. if (json.code === 0 && json.data && json.data.plans) {
  1039. this.vipPlans = json.data.plans;
  1040. this.showWxLogin = !!json.data.show_wx_login;
  1041. if (this.vipPlans.length > 0) {
  1042. this.selectedPayPlan = this.vipPlans[0]; // 默认选中第一个套餐
  1043. }
  1044. } else {
  1045. ToastUtil.showToast('套餐数据解析失败');
  1046. }
  1047. } else {
  1048. ToastUtil.showToast('获取套餐失败');
  1049. }
  1050. } catch (e) {
  1051. ToastUtil.showToast('获取套餐异常');
  1052. }
  1053. }
  1054. async doPay(plan: VipPlanApi) {
  1055. if (this.payType === 0) {
  1056. await this.doWxPay(plan);
  1057. } else if (this.payType === 1) {
  1058. await this.doAliPay(plan);
  1059. }
  1060. }
  1061. async doWxPay(plan: VipPlanApi) {
  1062. if (!(await isHasWX())) {
  1063. ToastUtil.showToast('没有安装微信');
  1064. return;
  1065. }
  1066. const appName = PreferencesUtil.getStringSync('appName', 'TTMusic');
  1067. hilog.info(0x0000, 'UserCenter', `doWxPay: current this.userId is ${this.userId}`);
  1068. const prepayId = await createWeChatOrder(CommonConstants.WX_PAY_APPID, CommonConstants.WX_PAY_MCHID,
  1069. appName, Math.round(Number(plan.price) * 100), pinyin4js.getShortPinyin(this.appName), this.userId, plan.id);
  1070. if (prepayId) {
  1071. await this.wechatPay(prepayId);
  1072. } else {
  1073. ToastUtil.showToast('微信下单失败');
  1074. }
  1075. }
  1076. async wechatPay(prepayId: string) {
  1077. let nonceStr = RandomUtil.getRandomStr(16, 'abcdefghijklmnopqrstuvwxyz');
  1078. const timestamp: number = Math.floor(Date.now() / 1000);
  1079. const signString = constructSignatureString(CommonConstants.WX_PAY_APPID, timestamp + '', nonceStr, prepayId);
  1080. let signStr = await rsaSign2048(signString);
  1081. let signStrBase64 = Base64Util.encodeToStrSync(signStr);
  1082. let req = new PayReq();
  1083. req.partnerId = CommonConstants.WX_PAY_MCHID;
  1084. req.appId = CommonConstants.WX_PAY_APPID;
  1085. req.packageValue = 'Sign=WXPay';
  1086. req.prepayId = prepayId;
  1087. req.nonceStr = nonceStr;
  1088. req.timeStamp = timestamp + '';
  1089. req.sign = signStrBase64;
  1090. req.extData = 'extData';
  1091. let finished = await this.wxApi.sendReq(getContext(this) as common.UIAbilityContext, req);
  1092. hilog.info(0x0000, 'UserCenter', 'send request finished: ', finished);
  1093. }
  1094. async doAliPay(plan: VipPlanApi) {
  1095. const appName = PreferencesUtil.getStringSync('appName', 'TTMusic');
  1096. OrderInfoUtil.getOrderInfo(Number(plan.price), appName, this.userId, plan.id).then((orderInfo) => {
  1097. console.log('Heanup: alipay orderInfo = ' + json.stringify(orderInfo))
  1098. new Pay().pay(orderInfo, true).then(async (result) => {
  1099. if (result.get('resultStatus') === '9000') {
  1100. ToastUtil.showToast('支付成功!');
  1101. void this.fetchUserInfo();
  1102. // 可在此处刷新会员状态
  1103. } else {
  1104. ToastUtil.showToast('取消支付!');
  1105. }
  1106. }).catch((error: BusinessError) => {
  1107. ToastUtil.showToast('支付失败!' + error.message);
  1108. });
  1109. });
  1110. }
  1111. // 每次进入页面时刷新用户数据
  1112. async fetchUserInfo() {
  1113. // console.log('Heanup 刷新用户信息:'+token)
  1114. await UserUtil.fetchUserInfo();
  1115. // console.log("Heanup UserInfo:" + json.stringify(user_info))
  1116. this.isLogin = PreferencesUtil.getBooleanSync('isLogin', false);
  1117. this.userId = PreferencesUtil.getNumberSync('userId', 0);
  1118. this.userName = PreferencesUtil.getStringSync('userName', '未登录用户');
  1119. this.userAvatarUrl = PreferencesUtil.getStringSync('userAvatarUrl', '');
  1120. this.subscriptionName = PreferencesUtil.getStringSync('subscriptionName', '');
  1121. this.subscriptionEndDate = PreferencesUtil.getStringSync('subscriptionEndDate', '');
  1122. this.hasActiveSubscription = PreferencesUtil.getBooleanSync('hasActiveSubscription', false);
  1123. // console.log('Heanup hasActiveSubscription:' + this.hasActiveSubscription)
  1124. // 新增:同步线上会员状态到本地,便于 Utility.isNoble 全局判断
  1125. PreferencesUtil.putSync('hasActiveSubscription', this.hasActiveSubscription);
  1126. }
  1127. // 微信回调处理
  1128. private onWXResp: OnWXResp = (resp): void => {
  1129. if (resp.errCode === ErrCode.ERR_OK) {
  1130. // 判断回调类型
  1131. if (resp.type === wxopensdk.Command.kCommandSendAuth) {
  1132. // 微信登录成功回调
  1133. const authResp = resp as wxopensdk.SendAuthResp; // 转换为授权响应类型,以便访问code
  1134. this.getUserInfoByCode(resp?.['code'])
  1135. DialogHelper.closeDialog('loginDialog');
  1136. ToastUtil.showToast('登录成功!');
  1137. } else if (resp.type === wxopensdk.Command.kCommandPay) {
  1138. // 微信支付成功回调
  1139. ToastUtil.showToast('支付成功!');
  1140. // 可在此处刷新会员状态或其他支付成功后的业务逻辑
  1141. // this.fetchVipPlans(); // 刷新会员套餐信息
  1142. this.fetchUserInfo();
  1143. } else {
  1144. // 其他类型的成功回调
  1145. ToastUtil.showToast('操作成功!');
  1146. }
  1147. } else {
  1148. // 处理错误情况
  1149. if (resp.type === wxopensdk.Command.kCommandSendAuth) {
  1150. ToastUtil.showToast('微信登录失败!');
  1151. } else if (resp.type === wxopensdk.Command.kCommandPay) {
  1152. ToastUtil.showToast('支付失败!');
  1153. } else {
  1154. ToastUtil.showToast('操作失败!');
  1155. }
  1156. }
  1157. }
  1158. }