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