| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074 |
- import { router } from '@kit.ArkUI';
- import { AppUtil, ToastUtil, PreferencesUtil, StrUtil } from '@pura/harmony-utils';
- import { BreakpointTypeEnum } from '../common/util/BreakpointSystem';
- import * as wxopensdk from '@tencent/wechat_open_sdk';
- import { OnWXResp, WXApi, WXEventHandler } from '../common/util/WXApiWrap';
- import { bundleManager, common, ConfigurationConstant } from '@kit.AbilityKit';
- import { ErrCode } from '@tencent/wechat_open_sdk';
- import { http } from '@kit.NetworkKit';
- import { authentication } from '@kit.AccountKit';
- import { hilog } from '@kit.PerformanceAnalysisKit';
- import { BusinessError } from '@kit.BasicServicesKit';
- import { util } from '@kit.ArkTS';
- import { DialogHelper } from '@pura/harmony-dialog';
- import { Pay } from '@cashier_alipay/cashiersdk';
- import { OrderInfoUtil } from '../alipay/OrderInfoUtil';
- import { RandomUtil, Base64Util } from '@pura/harmony-utils';
- import { CommonConstants } from '../common/constants/CommonConstants';
- import { PayReq } from '@tencent/wechat_open_sdk';
- import { cryptoFramework } from '@kit.CryptoArchitectureKit';
- import json from '@ohos.util.json';
- import UserUtil, { VipFeature, VipPlanApi, VipPlanListApiResponse, WeChatPrepayId } from '../common/util/UserUtil';
- import { Utility } from '../common/util/Utility';
- import { pinyin4js } from '@ohos/pinyin4js';
- // 微信支付相关工具方法
- async function isHasWX(): Promise<boolean> {
- try {
- let link = 'weixin://';
- let data = bundleManager.canOpenLink(link);
- if (data) {
- return true;
- } else {
- ToastUtil.showToast('微信未安装');
- return false;
- }
- } catch (err) {
- let message = (err as BusinessError).message;
- console.error('canOpenLink failed: %{public}s', message);
- return false;
- }
- }
- async function createWeChatOrder(app_id: string, mch_id: string, description: string, amount: number, out_trade_no_pre: string, uid: number, plan_id: string): Promise<string> {
- let requestUrl = CommonConstants.WX_PAY_API
- + '?app_id=' + encodeURIComponent(app_id.trim())
- + '&mch_id=' + encodeURIComponent(mch_id.trim())
- + '&description=' + encodeURIComponent(description.trim())
- + '&amount=' + encodeURIComponent(amount)
- + '&uid=' + encodeURIComponent(uid)
- + '&plan_id=' + encodeURIComponent(plan_id)
- + '&out_trade_no_pre=' + encodeURIComponent(out_trade_no_pre);
- const httpRequest: http.HttpRequest = http.createHttp();
- const options: http.HttpRequestOptions = {
- method: http.RequestMethod.GET,
- readTimeout: 6000,
- connectTimeout: 6000,
- };
- try {
- const response: http.HttpResponse = await httpRequest.request(requestUrl, options);
- if (response.responseCode === 200) {
- let responseData: WeChatPrepayId;
- if (typeof response.result === 'string') {
- responseData = JSON.parse(response.result) as WeChatPrepayId;
- } else if (response.result instanceof Object) {
- responseData = response.result as WeChatPrepayId;
- } else {
- throw new Error('Unexpected response format');
- }
- const prepayId: string = responseData.prepay_id;
- return prepayId;
- } else {
- ToastUtil.showToast('微信下单失败');
- return '';
- }
- } catch (error) {
- ToastUtil.showToast('微信下单异常');
- return '';
- }
- }
- function constructSignatureString(appId: string, timestamp: string, nonceStr: string, prepayId: string): string {
- return `${appId}\n${timestamp}\n${nonceStr}\n${prepayId}\n`;
- }
- async function rsaSign2048(message: string): Promise<Uint8Array> {
- try {
- let keyGen = cryptoFramework.createAsyKeyGenerator("RSA2048")
- let key = keyGen.convertPemKeySync(null, CommonConstants.WX_PAY_RSA_PRIVATE_KEY)
- let sign = cryptoFramework.createSign('RSA2048|PKCS1|SHA256');
- let msgBlob: cryptoFramework.DataBlob = {data: StrUtil.strToUint8Array(message)}
- sign.initSync(key.priKey)
- let signature = await sign.sign(msgBlob)
- return signature.data
- } catch (error) {
- console.error(error, `onecold error code: ${error.code}`)
- return new Uint8Array()
- }
- }
- // 1. 工具函数:将 #RRGGBB 字符串和透明度转为 'rgba(r,g,b,a)' 字符串,深色模式下可返回深色变体
- function themeColorWithAlpha(themeColor: string, alpha: number, isDarkMode: boolean = false): string {
- if (isDarkMode) {
- // 深色模式下返回更深的灰色或半透明黑色
- return `rgba(34,34,34,${alpha + 0.2 > 1 ? 1 : alpha + 0.2})`;
- }
- const color = themeColor.replace('#', '');
- const r = parseInt(color.substring(0, 2), 16);
- const g = parseInt(color.substring(2, 4), 16);
- const b = parseInt(color.substring(4, 6), 16);
- return `rgba(${r},${g},${b},${alpha})`;
- }
- @Component
- @Entry
- export struct UserCenter {
- @State userName: string = '未登录用户';
- @State userId: number = 0;
- @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 vipFeatures: VipFeature[] = [
- { icon: $r('app.media.wusunyinzhi'), title: '无损音质', description: '专享无损音乐支持,让每个音符都完美呈现', isVipOnly: true,hasCome:true },
- // { icon: $r('app.media.lrc'), title: '歌词下载', description: '支持歌词API在线下载LRC歌词,实现完美同步', isVipOnly: true },
- // { icon: $r('app.media.cover'), title: '封面API', description: '支持设置自动下载封面', isVipOnly: true },
- { icon: $r('app.media.theme'), title: '主题定制', description: '自定义播放器主题,打造独一无二的个性风格', isVipOnly: true,hasCome:true },
- { icon: $r('app.media.no_ad'), title: '无广告', description: '清爽界面,无广告播放器', isVipOnly: true ,hasCome:true},
- { icon: $r('app.media.skip'), title: '跳过头尾', description: '为某个歌单专门定制设置跳过头尾', isVipOnly: true ,hasCome:true},
- { icon: $r('app.media.car'), title: 'HiCAR播放', description: '专为驾车体验优化的音乐播放模式', isVipOnly: true ,hasCome:false},
- { icon: $r('app.media.music_wave'), title: '调音均衡器', description: '精准定制音效,带来极致听觉体验', isVipOnly: true ,hasCome:false},
- ];
- @State vipPlans: VipPlanApi[] = [];
- @State selectedPayPlan: VipPlanApi | null = null;
- @State payType: number = 0; // 0: 微信, 1: 支付宝
- @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
- @State isDarkMode: boolean = false
- @State webUrl: string = CommonConstants.NEW_MEMBER_AGREEMENTS
- @State appName:string = ''
- @StorageProp('topRectHeight') topRectHeight: number = px2vp(AppUtil.getStatusBarHeight());
- @StorageProp('currentColorMode') @Watch('onColorModeChange') currentMode: number =
- ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
- onColorModeChange() {
- this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
- }
- constructor() {
- super();
- }
- // 微信回调处理
- private onWXResp: OnWXResp = (resp): void => {
- if (resp.errCode === ErrCode.ERR_OK) {
- // 判断回调类型
- if (resp.type === wxopensdk.Command.kCommandSendAuth) {
- // 微信登录成功回调
- const authResp = resp as wxopensdk.SendAuthResp; // 转换为授权响应类型,以便访问code
- this.getUserInfoByCode(resp?.['code'])
- DialogHelper.closeDialog('loginDialog');
- ToastUtil.showToast('登录成功!');
- } else if (resp.type === wxopensdk.Command.kCommandPay) {
- // 微信支付成功回调
- ToastUtil.showToast('支付成功!');
- // 可在此处刷新会员状态或其他支付成功后的业务逻辑
- // this.fetchVipPlans(); // 刷新会员套餐信息
- this.fetchUserInfo();
- } else {
- // 其他类型的成功回调
- ToastUtil.showToast('操作成功!');
- }
- } else {
- // 处理错误情况
- if (resp.type === wxopensdk.Command.kCommandSendAuth) {
- ToastUtil.showToast('微信登录失败!');
- } else if (resp.type === wxopensdk.Command.kCommandPay) {
- ToastUtil.showToast('支付失败!');
- } else {
- ToastUtil.showToast('操作失败!');
- }
- }
- }
- // 通过code换取用户信息
- async getUserInfoByCode(code: string) {
- await UserUtil.getUserInfoByCode(code).then(async ()=>{
- // 登录后同步本地会员到线上(仅本地有会员且线上无会员时)
- Utility.getLocalNobleExpireDate().then((expireDate)=>{
- if (Utility.isNobleForOld() && !this.hasActiveSubscription && expireDate) {
- UserUtil.syncLocalNobleToServer(expireDate).then((success)=>{
- if (success) {
- ToastUtil.showToast('本地会员已同步到账号');
- PreferencesUtil.putSync('isNoble', false);
- this.fetchUserInfo();
- } else {
- this.fetchUserInfo();
- }
- });
- } else {
- this.fetchUserInfo();
- }
- });
- });
- }
- // 通过华为账号信息换取用户信息和会员状态
- async getUserInfoByHuawei(openID: string, unionID: string, nickname: string, avatarUri: string) {
- await UserUtil.getUserInfoByHuawei(openID, unionID, nickname, avatarUri).then(async (data)=>{
- // 登录后同步本地会员到线上(仅本地有会员且线上无会员时)
- Utility.getLocalNobleExpireDate().then((expireDate)=>{
- if (Utility.isNobleForOld() && !this.hasActiveSubscription && expireDate) {
- UserUtil.syncLocalNobleToServer(expireDate).then((success)=>{
- if (success) {
- ToastUtil.showToast('本地会员已同步到账号');
- // 关键:同步成功后再刷新用户信息
- this.fetchUserInfo();
- } else {
- // 同步失败也刷新一次
- this.fetchUserInfo();
- }
- });
- } else {
- // 不需要同步会员,直接刷新
- this.fetchUserInfo();
- }
- });
- })
- }
- @Builder
- syncNobleDialogContentBuilder(): void {
- Text('检测到您是本地会员,为保障权益请登录账号,会员权益将自动同步到您的账号。')
- .fontSize(16)
- .fontColor('#222')
- .width('100%')
- .textAlign(TextAlign.Center)
- .padding(16)
- }
- aboutToAppear(): void {
- Utility.getAppName(getContext(this)).then((appName:string)=>{
- this.appName = appName
- })
- this.topRectHeight = px2vp(AppUtil.getStatusBarHeight());
- let themeColor = PreferencesUtil.getStringSync('THEME_COLOR', CommonConstants.DEFAULT_THEME_COLOR);
- this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
- AppStorage.setOrCreate('themeColor', themeColor);
- this.themeColor = themeColor;
- this.isLogin = PreferencesUtil.getBooleanSync('isLogin', false);
- this.userId = PreferencesUtil.getNumberSync('userId', 0);
- 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.fetchVipPlans();
- if (this.isLogin) {
- void this.fetchUserInfo();
- }
- console.log('Heanup isLogin:'+this.isLogin)
- // 只在非同步跳转时弹窗提醒
- let params = router.getParams() as Record<string, Object>;
- let fromSyncNoble = params && params['fromSyncNoble'] === true;
- if (fromSyncNoble) {
- // 自动弹出登录框
- this.showLoginDialog();
- } else if (!this.isLogin && Utility.isNobleForOld()) {
- DialogHelper.showCustomContentDialog({
- dialogId: 'syncNobleDialog',
- title: '会员同步提示',
- contentBuilder: () => {
- this.syncNobleDialogContentBuilder();
- },
- buttons: [
- { value: '暂不登录' },
- {
- value: '去登录',
- action: () => {
- this.showLoginDialog();
- }
- }
- ]
- });
- }
- }
- aboutToDisappear() {
- this.wxEventHandler.unregisterOnWXRespCallback(this.onWXResp)
- }
- build() {
- Column() {
- // 顶部安全区和自定义标题栏
- Column() {
- // 顶部安全区
- Blank()
- .height(this.topRectHeight)
- .backgroundColor(this.isDarkMode?$r('app.color.title_bar_bg'):this.themeColor)
- .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
- // 自定义标题栏(Stack实现绝对居中)
- Stack() {
- // 居中标题
- Text('用户中心')
- .fontSize(18)
- .fontColor(Color.White)
- .align(Alignment.Center)
- // 左右按钮
- Row() {
- Image($r('app.media.left_back_white'))
- .width(32)
- .height(32)
- .margin({ left: 12, right: 8 })
- .onClick(() => {
- router.back();
- })
- Blank().flexGrow(1)
- Blank().width(32)
- }
- .height(48)
- .width('100%')
- .alignItems(VerticalAlign.Center)
- }
- .height(48)
- .width('100%')
- .backgroundColor(this.isDarkMode?$r('app.color.user_center_card_background'):this.themeColor)
- }
- Scroll() {
- Column() {
- this.buildUserInfoCard()
- this.buildVipPlans()
- this.buildVipFeatures()
- // this.buildFunctionMenu()
- }
- .width('100%')
- }
- .scrollBar(BarState.Off)
- .edgeEffect(EdgeEffect.Spring)
- .layoutWeight(1)
- .padding({
- bottom:45
- })
- }
- .backgroundColor($r('app.color.index_background'))
- .width('100%')
- .height('100%')
- .layoutWeight(1)
- }
- // 用户信息卡片
- @Builder
- buildUserInfoCard() {
- Column() {
- Row() {
- Image(this.userAvatarUrl && this.userAvatarUrl.length > 0 ? this.userAvatarUrl : this.userAvatar)
- .width(56)
- .height(56)
- .borderRadius('50%')
- .clip(true)
- .backgroundColor(this.isDarkMode ? Color.Black : Color.White)
- .shadow({ radius: 8, color: 0x11000000, offsetX: 0, offsetY: 2 })
- Column() {
- Row() {
- Text(this.isLogin ? this.userName : '未登录用户')
- .fontSize(17)
- .fontWeight(FontWeight.Bold)
- .fontColor(this.isDarkMode ? Color.White : Color.Black)
- .textAlign(TextAlign.Start)
- .maxLines(1)
- .width(110)
- .margin({ right: 12 })
- }
- .justifyContent(FlexAlign.Start)
- .margin({ top: 2 })
- if (this.isLogin) {
- if (this.hasActiveSubscription) {
- Text(this.subscriptionName)
- .fontSize(13)
- .fontColor( themeColorWithAlpha(this.themeColor, 0.8, false))
- .textAlign(TextAlign.Start)
- Text('到期:' + this.subscriptionEndDate)
- .fontSize(11)
- .fontColor(this.isDarkMode ? Color.White : Color.Gray)
- .textAlign(TextAlign.Start)
- } else {
- Text('普通用户')
- .fontSize(12)
- .fontColor(this.isDarkMode ? Color.White : Color.Grey)
- .textAlign(TextAlign.Start)
- }
- }
- }
- .alignItems(HorizontalAlign.Start)
- .width(155)
- .margin({ left: 10 })
- Column(){
- if (this.isLogin) {
- Button('退出')
- .fontColor( Color.White)
- .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
- .height(36)
- .width(66)
- .fontSize(13)
- .borderRadius(13)
- .margin({ left: 0 })
- .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(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
- .height(36)
- .width(66)
- .fontSize(13)
- .borderRadius(13)
- .margin({ left: 0 })
- }
- }
- .alignItems(HorizontalAlign.End)
- .margin({left:30, right: 10 })
- }
- .onClick(() => {
- if (!this.isLogin) {
- this.showLoginDialog();
- }
- })
- .width('100%')
- .padding(10)
- }
- .backgroundColor($r('app.color.user_center_card_background'))
- .borderRadius(24)
- .margin({ left: 14, right: 14, top: 15 })
- .shadow({ radius: 8, color: 0x11000000, offsetX: 0, offsetY: 2 })
- }
- // 登录方式选择弹窗内容
- @Builder
- loginDialogContentBuilder(): void {
- Column() {
- Row() {
- // 微信登录图标按钮
- Column() {
- Button({ type: ButtonType.Normal, stateEffect: true }) {
- Column() {
- Image($r('app.media.wx_logo'))
- .width(56)
- .height(56)
- .margin({ bottom: 8 })
- Text('微信登录')
- .fontSize(15)
- .fontWeight(FontWeight.Bold)
- .fontColor('#333')
- }
- .alignItems(HorizontalAlign.Center)
- }
- .width(100)
- .height(100)
- .backgroundColor(Color.White)
- // .border({ color: this.themeColor, width: 2 })
- .borderRadius(20)
- .onClick(async () => {
- this.payType = 0;
- // 微信登录
- 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)
- DialogHelper.closeDialog('loginDialog');
- })
- }
- .margin({ right: 24 })
- // 华为账号登录图标按钮
- Column() {
- Button({ type: ButtonType.Normal, stateEffect: true }) {
- Column() {
- Image($r('app.media.huawei'))
- .width(56)
- .height(56)
- .margin({ bottom: 8 })
- Text('华为登录')
- .fontSize(15)
- .fontWeight(FontWeight.Bold)
- .fontColor('#333')
- }
- .alignItems(HorizontalAlign.Center)
- }
- .width(100)
- .height(100)
- .backgroundColor(Color.White)
- // .border({ color: this.themeColor, width: 2 })
- .borderRadius(20)
- .onClick(() => {
- this.payType = 1;
- this.huaweiQuickLogin();
- DialogHelper.closeDialog('loginDialog');
- })
- }
- }
- .justifyContent(FlexAlign.Center)
- .width('100%')
- }
- .padding({ left: 24, right: 24, top: 8, bottom: 24 })
- .width('100%')
- }
- showLoginDialog() {
- DialogHelper.showCustomContentDialog({
- dialogId: 'loginDialog',
- title: '',
- autoCancel: true,
- contentBuilder: () => {
- this.loginDialogContentBuilder();
- },
- buttons: [
- ]
- });
- }
- // 会员权益展示
- @Builder
- buildVipFeatures(): void {
- Column() {
- Text('会员权益')
- .fontSize(18)
- .fontWeight(FontWeight.Bold)
- .padding({ top: 24, bottom: 16 })
- .fontColor(this.isDarkMode ? Color.White : '#191919')
- .textAlign(TextAlign.Center);
- Grid() {
- ForEach(this.vipFeatures, (feature: VipFeature) => {
- GridItem() {
- Column() {
- Row() {
- Blank().layoutWeight(1)
- if (feature.hasCome === false) {
- Text('开发中')
- .fontSize(10)
- .fontColor(themeColorWithAlpha(this.themeColor, 0.9, false))
- .backgroundColor(themeColorWithAlpha(this.themeColor, 0.15, this.isDarkMode))
- .borderRadius(8)
- .padding({ left: 6, right: 6, top: 2, bottom: 2 })
- .margin({ top: 2, right: 2 })
- }else {
- Text('已开发')
- .fontSize(10)
- .fontColor(themeColorWithAlpha(this.themeColor, 0.9, false))
- .backgroundColor(themeColorWithAlpha(this.themeColor, 0.15, this.isDarkMode))
- .borderRadius(8)
- .padding({ left: 6, right: 6, top: 2, bottom: 2 })
- .margin({ top: 2, right: 2 })
- }
- }
- Row() {
- Blank().layoutWeight(1)
- Image(feature.icon)
- .width(32)
- .height(32)
- .fillColor(this.themeColor)
- Blank().layoutWeight(1)
- }
- .margin({ bottom: 8 })
- Text(feature.title)
- .fontSize(15)
- .fontWeight(FontWeight.Bold)
- .fontColor(this.isDarkMode ? Color.White : '#191919')
- .margin({ bottom: 2 })
- Text(feature.description)
- .fontSize(12)
- .fontColor(this.isDarkMode ? Color.Gray : '#888')
- .textAlign(TextAlign.Center)
- .maxLines(2)
- .margin({ bottom: 4 })
- if (feature.isVipOnly) {
- Text('VIP专享')
- .fontSize(10)
- .fontColor(this.isDarkMode ? Color.White : this.themeColor)
- .backgroundColor(this.isDarkMode?$r('app.color.user_center_button_background'):themeColorWithAlpha(this.themeColor, 0.15, false))
- .borderRadius(8)
- .padding({ left: 6, right: 6, top: 2, bottom: 2 })
- .margin({ top: 2,bottom:6 })
- }
- }
- .backgroundColor($r('app.color.user_center_card_background'))
- .borderRadius(16)
- .shadow({ radius: 8, color: 0x11000000, offsetX: 0, offsetY: 2 })
- .border({ color:(this.isDarkMode?$r('app.color.user_center_button_background') :themeColorWithAlpha(this.themeColor, 0.08, false))//未选中时的边框颜色
- , width: 2 })
- .padding(12)
- .width('100%')
- .height(145)
- }
- })
- }
- .columnsTemplate('1fr 1fr')
- .columnsGap(16)
- .rowsGap(16)
- .margin({ left: 16, right: 16,top:0, bottom: 15 })
- }
- .backgroundColor(this.isDarkMode ? Color.Black : Color.White)
- .borderRadius(24)
- .margin({ left: 16, right: 16,top:15, bottom: 15 })
- }
- // 支付方式选择弹窗内容
- @Builder
- payDialogContentBuilder(): void {
- Column() {
- Row() {
- Image($r('app.media.wx_logo'))
- .width(22)
- .height(22)
- .alignSelf(ItemAlign.Center)
- .margin({ left: 25 })
- Text('微信支付')
- .margin({ left: 10, right: 20 })
- .fontSize(15)
- .fontColor(Color.Gray)
- .fontWeight(480)
- Blank()
- Checkbox()
- .select(this.payType === 0)
- .selectedColor(this.themeColor)
- .onChange((checked: boolean) => {
- if (checked) {
- this.payType = 0
- }
- })
- .shape(CheckBoxShape.CIRCLE)
- .margin({ left: 20, top: 8, bottom: 8, right: 20 })
- .width(22)
- .height(22)
- }
- .onClick(() => {
- this.payType = 0
- })
- .width('100%')
- .height(50)
- Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
- Row() {
- Image($r('app.media.alipay'))
- .width(22)
- .height(22)
- .alignSelf(ItemAlign.Center)
- .margin({ left: 25 })
- Text('支付宝')
- .margin({ left: 10, right: 20 })
- .fontSize(15)
- .fontColor(Color.Gray)
- .fontWeight(480)
- Blank()
- Checkbox()
- .select(this.payType === 1)
- .selectedColor(this.themeColor)
- .onChange((checked: boolean) => {
- if (checked) {
- this.payType = 1
- }
- })
- .shape(CheckBoxShape.CIRCLE)
- .margin({ left: 20, top: 8, bottom: 8, right: 20 })
- .width(22)
- .height(22)
- }
- .onClick(() => {
- this.payType = 1
- })
- .width('100%')
- .height(50)
- }
- .padding(24)
- .width('100%')
- }
- // 会员套餐选择
- @Builder
- buildVipPlans(): void {
- Column() {
- Text('会员套餐')
- .textAlign(TextAlign.Start)
- .padding({ left: 16, bottom: 15, top: 18 })
- .fontSize(18)
- .fontColor(this.isDarkMode ? Color.White : '#191919')
- .fontWeight(FontWeight.Bold)
- .fontFamily('鸿蒙黑体');
- // 横向滑动卡片区
- List() {
- ForEach(this.vipPlans, (plan: VipPlanApi, index: number) => {
- ListItem() {
- Column() {
- Text(plan.name)
- .fontSize(15)
- .fontWeight(FontWeight.Bold)
- .fontColor($r('app.color.user_center_font'))
- .margin({ top: 18, bottom: 8 })
- .textAlign(TextAlign.Center)
- // 价格上下排列
- Column() {
- Text('¥' + plan.price)
- .fontSize(24)
- .fontWeight(FontWeight.Bold)
- .fontColor(this.themeColor)
- .margin({ bottom: (plan.original_price && plan.discount_percent > 0) ? 2 : 0 })
- .textAlign(TextAlign.Center)
- if (plan.original_price && plan.discount_percent && plan.discount_percent > 0) {
- Text('¥' + plan.original_price)
- .fontSize(14)
- .fontColor(themeColorWithAlpha(this.themeColor, 0.7,false))
- .decoration({ type: TextDecorationType.LineThrough, color: themeColorWithAlpha(this.themeColor, 0.7, false) })
- .fontStyle(FontStyle.Italic)
- .textAlign(TextAlign.Center)
- }
- }
- .alignItems(HorizontalAlign.Center)
- .margin({ bottom: 6 })
- Text(plan.duration === '0' ? '永久会员' : (plan.duration + '天会员'))
- .fontSize(13)
- .fontColor(this.isDarkMode ? Color.White : Color.Grey)
- .maxLines(1)
- .textAlign(TextAlign.Center)
- }
- .backgroundColor(this.selectedPayPlan && this.selectedPayPlan.id === plan.id ? themeColorWithAlpha(this.themeColor, 0.08, false) : (this.isDarkMode ? Color.Black : '#F7F7F7'))
- .borderRadius(18)
- .shadow({ radius: 8, color: 0x11000000, offsetX: 0, offsetY: 2 })
- .border({ color: this.selectedPayPlan && this.selectedPayPlan.id === plan.id ?
- (this.isDarkMode ? Color.White :themeColorWithAlpha(this.themeColor, 0.3, this.isDarkMode))//选中时的边框颜色
- :
- (this.isDarkMode?$r('app.color.user_center_button_background') :themeColorWithAlpha(this.themeColor, 0.08, this.isDarkMode))//未选中时的边框颜色
- , width: 2 })
- .width(125)
- .height(125)
- .margin({ left: index === 0 ? 16 : 8, right: index === this.vipPlans.length - 1 ? 16 : 8 })
- .onClick(() => {
- this.selectedPayPlan = plan;
- })
- .alignItems(HorizontalAlign.Center)
- }
- })
- }
- .listDirection(Axis.Horizontal)
- .height(130)
- .edgeEffect(EdgeEffect.None)
- .scrollBar(BarState.Off)
- .margin({ bottom: 18 })
- Button(this.isVip ? '升级会员' : '立即开通')
- .borderRadius(24)
- .backgroundColor(this.isDarkMode ? $r('app.color.user_center_button_background') : this.themeColor)
- .width(this.currentBreakpoint !== BreakpointTypeEnum.SM?'25%':'60%')
- .fontColor(Color.White)
- .fontSize(18)
- .padding(12)
- .fontWeight(FontWeight.Bold)
- .margin({ top: 1, bottom: 15 })
- .alignSelf(ItemAlign.Center)
- .onClick(() => {
- if (!this.isLogin) {
- ToastUtil.showToast('请先登录');
- return;
- }
- if (!this.selectedPayPlan) {
- ToastUtil.showToast('请选择套餐');
- return;
- }
- DialogHelper.showCustomContentDialog({
- dialogId: 'payDialog',
- title: '请选择支付方式',
- autoCancel: true,
- contentBuilder: () => {
- this.payDialogContentBuilder();
- },
- buttons: [
- { value: '取消' },
- {
- value: '确认支付',
- action: () => {
- if (this.selectedPayPlan) {
- void this.doPay(this.selectedPayPlan);
- }
- DialogHelper.closeDialog('payDialog');
- }
- }
- ]
- });
- });
- Row() {
- Text('购买前请阅读')
- .fontSize(12)
- .fontColor(this.isDarkMode ? Color.White : '#888')
- Text('《会员协议》')
- .fontSize(12)
- .fontColor(this.isDarkMode ? Color.Blue : this.themeColor)
- .onClick(() => {
- router.pushUrl({
- url: 'pages/WebIndex',
- params: { titleName: '会员协议', webUrl: this.webUrl }
- });
- // router.pushUrl({ url: this.webUrl });
- });
- }
- .justifyContent(FlexAlign.Center)
- .width('100%')
- .padding({ bottom: 16 });
- }
- .margin({ left: 14, right: 14, top: 15 })
- .backgroundColor($r('app.color.user_center_card_background'))
- .borderRadius(24)
- }
- // 功能菜单
- @Builder
- buildFunctionMenu(): void {
- Column() {
- this.menuItem($r('app.media.icon_favorite'), '我的收藏', () => {
- if (!this.isLogin) {
- ToastUtil.showToast('请先登录')
- return
- }
- // 跳转到收藏页面
- ToastUtil.showToast('敬请期待')
- })
- Line().width('90%').height(0.5).backgroundColor(0xFFEFEFEF)
- this.menuItem($r('app.media.icon_history'), '播放历史', () => {
- if (!this.isLogin) {
- ToastUtil.showToast('请先登录')
- return
- }
- // 跳转到历史记录页面
- ToastUtil.showToast('敬请期待')
- })
- Line().width('90%').height(0.5).backgroundColor(0xFFEFEFEF)
- this.menuItem($r('app.media.setting_red'), '设置', () => {
- // 跳转到设置页面
- router.pushUrl({
- url: 'pages/SettingPage'
- });
- })
- }
- .backgroundColor(Color.White)
- .borderRadius(24)
- .margin({ left: 20, right: 20, top: 24 })
- }
- // 华为一键登录
- async huaweiQuickLogin() {
- const logTag = 'UserCenter';
- const domainId = 0x0000;
- try {
- const authRequest = new authentication.HuaweiIDProvider().createAuthorizationWithHuaweiIDRequest();
- authRequest.scopes = ['profile'];
- authRequest.permissions = ['idtoken'];
- authRequest.forceAuthorization = true;
- authRequest.state = util.generateRandomUUID();
- authRequest.idTokenSignAlgorithm = authentication.IdTokenSignAlgorithm.PS256;
- const controller = new authentication.AuthenticationController(getContext(this));
- const response: authentication.AuthorizationWithHuaweiIDResponse = await controller.executeRequest(authRequest);
- const credential = response.data;
- const nickName = credential?.nickName ?? '华为用户';
- const avatarUri = credential?.avatarUri ?? '';
- const openID = credential?.openID;
- const unionID = credential?.unionID ?? '';
- hilog.info(domainId, logTag, `华为登录成功,昵称: ${nickName}, openID: ${openID}`);
- // 调用接口获取用户信息和会员状态
- if (openID) {
- await this.getUserInfoByHuawei(openID, unionID, nickName, avatarUri);
- } else {
- ToastUtil.showToast('openID获取失败');
- }
- } catch (error) {
- hilog.error(domainId, logTag, `华为登录失败: ${error.code}, ${error.message}`);
- this.dealHuaweiLoginError(error as BusinessError);
- ToastUtil.showToast('华为登录失败');
- }
- }
- dealHuaweiLoginError(error: BusinessError): void {
- if (error.code === 1001) {
- ToastUtil.showToast('未登录华为账号');
- } else if (error.code === 1002) {
- ToastUtil.showToast('网络异常');
- } else if (error.code === 1003) {
- ToastUtil.showToast('内部错误');
- } else if (error.code === 1004) {
- ToastUtil.showToast('用户取消授权');
- } else if (error.code === 1005) {
- ToastUtil.showToast('系统服务异常');
- } else if (error.code === 1006) {
- ToastUtil.showToast('请求被拒绝');
- } else if (error.code === 1007) {
- ToastUtil.showToast('无权限');
- } else {
- ToastUtil.showToast('登录失败');
- }
- }
- // 菜单项构建
- @Builder
- menuItem(icon: Resource, text: string, onClick: () => void): 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)
- }
- async fetchVipPlans() {
- try {
- const url = 'https://pay.ss5.xyz/subscription/get_list';
- 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;
- let json: VipPlanListApiResponse = JSON.parse(res) as VipPlanListApiResponse;
- if (json.code === 0 && json.data && json.data.plans) {
- this.vipPlans = json.data.plans;
- if (this.vipPlans.length > 0) {
- this.selectedPayPlan = this.vipPlans[0]; // 默认选中第一个套餐
- }
- } else {
- ToastUtil.showToast('套餐数据解析失败');
- }
- } else {
- ToastUtil.showToast('获取套餐失败');
- }
- } catch (e) {
- ToastUtil.showToast('获取套餐异常');
- }
- }
- async doPay(plan: VipPlanApi) {
- if (this.payType === 0) {
- await this.doWxPay(plan);
- } else if (this.payType === 1) {
- await this.doAliPay(plan);
- }
- }
- async doWxPay(plan: VipPlanApi) {
- if (!(await isHasWX())) {
- ToastUtil.showToast('没有安装微信');
- return;
- }
- const appName = PreferencesUtil.getStringSync('appName', 'TTMusic');
- hilog.info(0x0000, 'UserCenter', `doWxPay: current this.userId is ${this.userId}`);
- const prepayId = await createWeChatOrder(CommonConstants.WX_PAY_APPID, CommonConstants.WX_PAY_MCHID,
- appName, Number(plan.price) * 100, pinyin4js.getShortPinyin(this.appName), this.userId, plan.id);
- if (prepayId) {
- await this.wechatPay(prepayId);
- } else {
- ToastUtil.showToast('微信下单失败');
- }
- }
- async wechatPay(prepayId: string) {
- let nonceStr = RandomUtil.getRandomStr(16, 'abcdefghijklmnopqrstuvwxyz');
- const timestamp: number = Math.floor(Date.now() / 1000);
- const signString = constructSignatureString(CommonConstants.WX_PAY_APPID, timestamp + '', nonceStr, prepayId);
- let signStr = await rsaSign2048(signString);
- let signStrBase64 = Base64Util.encodeToStrSync(signStr);
- let req = new PayReq();
- req.partnerId = CommonConstants.WX_PAY_MCHID;
- req.appId = CommonConstants.WX_PAY_APPID;
- req.packageValue = 'Sign=WXPay';
- req.prepayId = prepayId;
- req.nonceStr = nonceStr;
- req.timeStamp = timestamp + '';
- req.sign = signStrBase64;
- req.extData = 'extData';
- let finished = await this.wxApi.sendReq(getContext(this) as common.UIAbilityContext, req);
- hilog.info(0x0000, 'UserCenter', 'send request finished: ', finished);
- }
- async doAliPay(plan: VipPlanApi) {
- const appName = PreferencesUtil.getStringSync('appName', 'TTMusic');
- OrderInfoUtil.getOrderInfo(Number(plan.price), appName, this.userId, plan.id).then((orderInfo) => {
- console.log('Heanup: alipay orderInfo = '+json.stringify(orderInfo))
- new Pay().pay(orderInfo, true).then(async (result) => {
- if (result.get('resultStatus') === '9000') {
- ToastUtil.showToast('支付成功!');
- void this.fetchUserInfo();
- // 可在此处刷新会员状态
- } else {
- ToastUtil.showToast('取消支付!');
- }
- }).catch((error: BusinessError) => {
- ToastUtil.showToast('支付失败!' + error.message);
- });
- });
- }
- // 每次进入页面时刷新用户数据
- async fetchUserInfo() {
- // console.log('Heanup 刷新用户信息:'+token)
- await UserUtil.fetchUserInfo().then((user_info)=>{
- console.log("Heanup UserInfo:"+json.stringify(user_info))
- this.isLogin = PreferencesUtil.getBooleanSync('isLogin', false);
- this.userId = PreferencesUtil.getNumberSync('userId', 0);
- 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);
- console.log('Heanup hasActiveSubscription:'+this.hasActiveSubscription)
- // 新增:同步线上会员状态到本地,便于 Utility.isNoble 全局判断
- PreferencesUtil.putSync('hasActiveSubscription', this.hasActiveSubscription);
- });
- }
- }
|