初始化
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/hj_utils/date_time_util.dart';
|
||||
import 'package:hgdj/tools_base/loading/loading_center_widget.dart';
|
||||
import 'package:hgdj/tools_base/refresh/pull_refresh.dart';
|
||||
|
||||
import '../../../hj_model/mine/credit_record_model.dart';
|
||||
import 'integral_record_logic.dart';
|
||||
|
||||
class ExchangeVipPage extends StatefulWidget {
|
||||
const ExchangeVipPage({super.key});
|
||||
|
||||
@override
|
||||
State<ExchangeVipPage> createState() => _ExchangeVipPageState();
|
||||
}
|
||||
|
||||
class _ExchangeVipPageState extends State<ExchangeVipPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('兑换记录')),
|
||||
backgroundColor: Colors.black,
|
||||
body: Stack(
|
||||
children: [
|
||||
GetBuilder<IntegralRecordLogic>(
|
||||
init: IntegralRecordLogic(),
|
||||
builder: (_) => Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 28, horizontal: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: pullYsRefresh(
|
||||
onInit: (controller) =>
|
||||
_.refreshController = controller,
|
||||
onRefresh: (controller) => _.loadData(),
|
||||
onLoading: (controller) => _.loadMoreData(),
|
||||
child: CustomScrollView(
|
||||
keyboardDismissBehavior:
|
||||
ScrollViewKeyboardDismissBehavior.onDrag,
|
||||
slivers: <Widget>[
|
||||
...[
|
||||
_buildHistoryTable(_),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHistoryTable(IntegralRecordLogic logic) {
|
||||
if (logic.isLoadingHistoryData) {
|
||||
return SliverToBoxAdapter(child: LoadingCenterWidget());
|
||||
} else if (logic.groupList.isEmpty) {
|
||||
return SliverToBoxAdapter(
|
||||
child: CErrorWidget(errorMsg: "暂无兑换记录"),
|
||||
);
|
||||
} else {
|
||||
return SliverList.separated(
|
||||
itemBuilder: (context, index) {
|
||||
return _buildListItem(logic.groupList[index]);
|
||||
},
|
||||
itemCount: logic.groupList.length,
|
||||
separatorBuilder: (context, index) {
|
||||
return Divider(
|
||||
height: 1,
|
||||
color: Colors.black87.withValues(alpha: 0.1),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildListItem(CreditRecordModel item) {
|
||||
String desc = "";
|
||||
if (item.desc?.isNotEmpty == true) {
|
||||
desc = item.desc ?? "-";
|
||||
List? list = desc.split("-");
|
||||
if (list.isNotEmpty == true && list.length > 1) {
|
||||
desc = list[1];
|
||||
}
|
||||
}
|
||||
return SizedBox(
|
||||
height: 44,
|
||||
child: Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.desc ?? '',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500),
|
||||
),
|
||||
Text(
|
||||
DateTimeUtil.utcTurnYear(item.createdAt),
|
||||
style: TextStyle(color: Color(0xFF999999), fontSize: 12),
|
||||
)
|
||||
],
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
"${item.integral ?? 0}积分",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: Color(0xFFF68804),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
//渐变文字
|
||||
class GradientText extends StatelessWidget {
|
||||
const GradientText(
|
||||
this.text, {super.key,
|
||||
required this.gradient,
|
||||
this.style,
|
||||
});
|
||||
|
||||
final String text;
|
||||
final TextStyle? style;
|
||||
final Gradient gradient;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ShaderMask(
|
||||
blendMode: BlendMode.srcIn,
|
||||
shaderCallback: (bounds) => gradient.createShader(
|
||||
Rect.fromLTWH(0, 0, bounds.width, bounds.height),
|
||||
),
|
||||
child: Text(text, style: style),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../hj_model/mine/task_center_data.dart';
|
||||
import '../../../hj_utils/api_service/mine_service.dart';
|
||||
import '../../../tools_base/global_store/store.dart';
|
||||
|
||||
class IntegralExchangeLogic extends GetxController {
|
||||
List<IntegralExchangeModel>? dataList;
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
loadData();
|
||||
}
|
||||
|
||||
loadData() async {
|
||||
final res = await MineService.fetchExchangeList();
|
||||
dataList ??= [];
|
||||
globalStore.refreshWallet();
|
||||
dataList?.addAll(res);
|
||||
update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/hj_model/mine/task_center_data.dart';
|
||||
import 'package:hgdj/hj_utils/api_service/mine_service.dart';
|
||||
import 'package:hgdj/tools_base/toast.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
import 'package:hgdj/tools_base/global_store/store.dart';
|
||||
import 'package:hgdj/tools_base/loading/loading_center_widget.dart';
|
||||
|
||||
import '../../../routers/jump_router.dart';
|
||||
import '../../../tools_base/loading/loading_helper.dart';
|
||||
import '../../../tools_base/widget/common_dialog.dart';
|
||||
import '../../../tools_base/widget/net_image_widget.dart';
|
||||
import 'integral_exchange_logic.dart';
|
||||
|
||||
class IntegralExchangePage extends StatelessWidget {
|
||||
const IntegralExchangePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<IntegralExchangeLogic>(
|
||||
init: IntegralExchangeLogic(),
|
||||
global: false,
|
||||
builder: (logic) {
|
||||
if (logic.dataList == null) return LoadingCenterWidget();
|
||||
if (logic.dataList!.isEmpty) return CErrorWidget();
|
||||
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.fromLTRB(16, 0, 16, 20),
|
||||
physics: const ClampingScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 10.w,
|
||||
mainAxisSpacing: 14.h,
|
||||
childAspectRatio: 174 / 111,
|
||||
),
|
||||
itemCount: logic.dataList?.length ?? 0,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final model = logic.dataList?[index];
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xff303030),
|
||||
borderRadius: BorderRadius.circular(9),
|
||||
),
|
||||
padding: EdgeInsets.only(
|
||||
left: 12.w, right: 12.w, top: 16.h, bottom: 9.h),
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
borderRadius:
|
||||
BorderRadius.vertical(top: Radius.circular(9)),
|
||||
child: Stack(
|
||||
alignment: Alignment.centerLeft,
|
||||
children: [
|
||||
NetworkImageLoader(
|
||||
imageUrl: model?.img ?? '',
|
||||
borderRadius: 0,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
10.sizeBoxH,
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'花费${model?.price ?? 0}积分',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF989898), fontSize: 14.sp),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () => onExchange(model!),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 9.w, vertical: 3.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xffF68804),
|
||||
borderRadius: BorderRadius.circular(11)),
|
||||
child: Text(
|
||||
'立即兑换',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF141414), fontSize: 12.sp),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
onExchange(IntegralExchangeModel model) async {
|
||||
final hasIntegral = globalStore.wallet?.integral ?? 0;
|
||||
if (hasIntegral < (model.price ?? 0)) {
|
||||
showToast('积分不足,无法兑换~');
|
||||
return;
|
||||
}
|
||||
LoadingHelper.showLoading();
|
||||
final res = await MineService.integralExchange(model.id ?? '');
|
||||
LoadingHelper.dismissLoading();
|
||||
if (res) {
|
||||
if (model.type == 5) {
|
||||
Get.dialog(InputAddressAlert(model: model));
|
||||
} else {
|
||||
showToast('兑换成功~');
|
||||
}
|
||||
globalStore.refreshWallet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InputAddressAlert extends StatefulWidget {
|
||||
// final Function()? confirmCallback;
|
||||
final IntegralExchangeModel model;
|
||||
const InputAddressAlert({super.key, required this.model});
|
||||
|
||||
@override
|
||||
State<InputAddressAlert> createState() => _InputAddressAlertState();
|
||||
}
|
||||
|
||||
class _InputAddressAlertState extends State<InputAddressAlert> {
|
||||
final nameCtr = TextEditingController();
|
||||
final addressCtr = TextEditingController();
|
||||
final phoneCtr = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CommonDialog(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'温馨提示',
|
||||
style: TextStyle(
|
||||
color: Color(0xffffffff),
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w800),
|
||||
),
|
||||
20.h.sizeBoxH,
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Colors.white.withValues(alpha: .1),
|
||||
),
|
||||
20.h.sizeBoxH,
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 45.h,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.symmetric(horizontal: 13.w),
|
||||
child: Text(
|
||||
"兑换成功!",
|
||||
style: TextStyle(color: Color(0x8CFFFFFF), fontSize: 18),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
5.h.sizeBoxH,
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 45.h,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.symmetric(horizontal: 13.w),
|
||||
child: Text(
|
||||
"请联系客服填写收货地址",
|
||||
style: TextStyle(color: Color(0x8CFFFFFF), fontSize: 18),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
20.h.sizeBoxH,
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.w),
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
pushToCustomService();
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xffF68804),
|
||||
borderRadius: BorderRadius.circular(6)),
|
||||
height: 48,
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'联系客服',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/hj_utils/api_service/mine_service.dart';
|
||||
import 'package:hgdj/tools_base/debug_log.dart';
|
||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||
|
||||
import '../../../hj_model/mine/credit_record_model.dart';
|
||||
|
||||
class IntegralRecordLogic extends GetxController {
|
||||
List<CreditRecordModel> groupList = [];
|
||||
bool isLoadingHistoryData = true;
|
||||
RefreshController? refreshController; //由 pullYsRefresh 的 onInit 注入,组件负责释放
|
||||
int currentPage = 1;
|
||||
|
||||
IntegralRecordLogic();
|
||||
|
||||
@override
|
||||
onReady() {
|
||||
super.onReady();
|
||||
loadData();
|
||||
}
|
||||
|
||||
void loadData({int page = 1}) async {
|
||||
try {
|
||||
final res =
|
||||
await MineService.getCreditRecords(pageNumber: page, pageSize: 20);
|
||||
if (res.isNotEmpty) {
|
||||
if (page == 1) groupList.clear();
|
||||
currentPage = page;
|
||||
groupList.addAll(res);
|
||||
}
|
||||
refreshController?.refreshCompleted();
|
||||
(res.length) < 20
|
||||
? refreshController?.loadNoData()
|
||||
: refreshController?.loadComplete();
|
||||
} catch (e) {
|
||||
refreshController?.refreshCompleted();
|
||||
refreshController?.loadComplete();
|
||||
debugLog(e);
|
||||
}
|
||||
update();
|
||||
isLoadingHistoryData = false;
|
||||
}
|
||||
|
||||
loadMoreData() => loadData(page: currentPage + 1);
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:hgdj/hj_model/video_model.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
import 'package:hgdj/tools_base/global_store/store.dart';
|
||||
import 'package:hgdj/tools_base/toast.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../home/widget/user_avatar.dart';
|
||||
import '../../home/widget/user_name_view.dart';
|
||||
import '../collec_history_buy/col_his_buy_page.dart';
|
||||
import '../home_mine_logic.dart';
|
||||
import '../mine_following/mine_following_view.dart';
|
||||
import '../mine_setting/mine_setting_page.dart';
|
||||
import '../mine_video_cache/video_cache_page.dart';
|
||||
import '../mine_vip/mine_charge_coin_page.dart';
|
||||
import '../mine_vip/mine_charge_vip_page.dart';
|
||||
import '../welfare/sign_daily_page.dart';
|
||||
import '../../ai/ai_girl/ai_h5_page.dart';
|
||||
import '../../../hj_utils/api_service/ai_service.dart';
|
||||
import '../../../tools_base/loading/loading_alert_widget.dart';
|
||||
|
||||
class MineInfoWidget extends StatelessWidget {
|
||||
final MineMainLogic logic;
|
||||
const MineInfoWidget({super.key, required this.logic});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(left: 16, top: 12),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
UserAvatar(
|
||||
size: 60,
|
||||
showBorder: false,
|
||||
model: Publisher()
|
||||
..portrait = logic.userInfo?.portrait
|
||||
..vipLevel = (logic.userInfo?.isVip == true) ? 1 : 0,
|
||||
onTap: () {
|
||||
Get.to(() => const MineSettingPage());
|
||||
},
|
||||
),
|
||||
12.sizeBoxW,
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Consumer<GlobalStore>(
|
||||
builder: (context, store, child) {
|
||||
final vipIcon = store.meInfo?.vipImageName ?? '';
|
||||
return Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: UserNameView(
|
||||
name: logic.userInfo?.name ?? '',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
isVip: (logic.userInfo?.vipLevel ?? 0) > 0,
|
||||
nameColor: Colors.white,
|
||||
),
|
||||
),
|
||||
8.sizeBoxW,
|
||||
if (store.isVIP && vipIcon.isNotEmpty)
|
||||
Image.asset(vipIcon, height: 20),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
5.sizeBoxH,
|
||||
InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(
|
||||
text: (logic.userInfo?.uid ?? 0).toString()));
|
||||
showToast('复制成功');
|
||||
},
|
||||
child: Text(
|
||||
"ID ${logic.userInfo?.uid ?? ''} 复制",
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .45),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () => Get.to(SignDailyPage()),
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: 60,
|
||||
padding: EdgeInsets.symmetric(vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF68804),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(15),
|
||||
bottomLeft: Radius.circular(15))),
|
||||
margin: EdgeInsets.only(left: 20, top: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text("签到", style: TextStyle(color: Colors.white)),
|
||||
],
|
||||
)),
|
||||
Container(
|
||||
width: 30,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.symmetric(vertical: 2, horizontal: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF52C56),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 0),
|
||||
child: Text(
|
||||
"有奖",
|
||||
style: TextStyle(color: Colors.white, fontSize: 10),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//充值/代理赚钱/ai科技入口
|
||||
class MineTaskMenuWidget extends StatelessWidget {
|
||||
static const double _overlap = 3;
|
||||
|
||||
MineTaskMenuWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16),
|
||||
width: double.infinity,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
// 相邻卡片重叠 _overlap:单卡宽度 = (总宽 + 两段重叠) / 3
|
||||
final itemW = (constraints.maxWidth + _overlap * 2) / 3;
|
||||
final items = [
|
||||
InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () =>
|
||||
Get.to(() => MineChargeVipPage(), preventDuplicates: false),
|
||||
child: _buildItem("", 'mine_vip_enter.webp'.mineImgPath, 0),
|
||||
),
|
||||
InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () {
|
||||
globalStore.refreshWallet();
|
||||
Get.to(() => const MineChargeCoinPage());
|
||||
},
|
||||
child: _buildItem(
|
||||
"余额:${(globalStore.wallet?.amount ?? 0) + (globalStore.wallet?.income ?? 0)}",
|
||||
'mine_gold_enter.webp'.mineImgPath,
|
||||
15,
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: _openAiGirlFriendDetail,
|
||||
child: _buildItem("", 'mine_ai_enter.webp'.mineImgPath, 0),
|
||||
),
|
||||
];
|
||||
return Stack(
|
||||
children: [
|
||||
// 用第一张图撑起 Stack 高度(等比例)
|
||||
Opacity(
|
||||
opacity: 0,
|
||||
child: SizedBox(width: itemW, child: items.first),
|
||||
),
|
||||
for (int i = 0; i < items.length; i++)
|
||||
Positioned(
|
||||
left: i * (itemW - _overlap),
|
||||
width: itemW,
|
||||
child: items[i],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 拉 AI 女友链接后直接进详情 H5
|
||||
Future<void> _openAiGirlFriendDetail() async {
|
||||
LoadingAlertWidget.show();
|
||||
try {
|
||||
final urlModel = await AIService.getMateUrl();
|
||||
LoadingAlertWidget.cancel();
|
||||
final url = urlModel?.url;
|
||||
if (url == null || url.isEmpty) {
|
||||
showToast('没有可以打开的AI女友,请联系客服~');
|
||||
return;
|
||||
}
|
||||
await Get.to(() => AiH5Page(webUrl: url));
|
||||
} catch (_) {
|
||||
LoadingAlertWidget.cancel();
|
||||
showToast('打开失败,请稍后重试');
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildItem(String title, String imagePath, double left) {
|
||||
return Stack(
|
||||
children: [
|
||||
// 宽度铺满,高度按图片比例自适应,避免 fill 拉伸变形
|
||||
Image.asset(
|
||||
imagePath,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.fitWidth,
|
||||
),
|
||||
if (title.isNotEmpty)
|
||||
Positioned(
|
||||
left: left,
|
||||
bottom: 10,
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.white.withValues(alpha: .55),
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MineFunchtionGridView extends StatelessWidget {
|
||||
MineFunchtionGridView({super.key});
|
||||
final functionLabels = ['我的收藏', '历史记录', '我的下载', '我的关注'];
|
||||
final functionImgs = [
|
||||
'mine_post.png',
|
||||
'mine_collect.png',
|
||||
'mine_follow.png',
|
||||
'mine_offical.png'
|
||||
];
|
||||
final functions = [
|
||||
() => ColHisBuyPage.to(PageType.collect),
|
||||
() => ColHisBuyPage.to(PageType.history),
|
||||
() => Get.to(() => VideoCachePage()),
|
||||
() => Get.to(() => MineFollowingPage()),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
color: Colors.white.withValues(alpha: .05),
|
||||
),
|
||||
child: GridView.builder(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.zero,
|
||||
shrinkWrap: true,
|
||||
itemCount: functionLabels.length,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4,
|
||||
crossAxisSpacing: 0,
|
||||
mainAxisSpacing: 0,
|
||||
childAspectRatio: 1,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
String titleDesc = functionLabels[index];
|
||||
String imagePath = functionImgs[index];
|
||||
|
||||
return InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: functions[index],
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(imagePath.mineImgPath, width: 24),
|
||||
5.sizeBoxH,
|
||||
Text(
|
||||
titleDesc,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:hgdj/hj_utils/api_service/mine_service.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
import 'package:hgdj/tools_base/global_store/store.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
import '../../../config/address.dart';
|
||||
import '../../../config/config.dart';
|
||||
import 'package:hgdj/tools_base/toast.dart';
|
||||
import '../../../hj_utils/image_util.dart';
|
||||
import '../../../tools_base/widget/net_image_widget.dart';
|
||||
|
||||
class MineShareQRView extends StatefulWidget {
|
||||
const MineShareQRView({super.key});
|
||||
|
||||
@override
|
||||
State<MineShareQRView> createState() => _MineShareQRViewState();
|
||||
}
|
||||
|
||||
class _MineShareQRViewState extends State<MineShareQRView> {
|
||||
final boundaryKey = GlobalKey();
|
||||
int totalInvite = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
final record = await MineService.getBindRecord(1, 1);
|
||||
totalInvite = record?.total ?? 0;
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
RepaintBoundary(
|
||||
key: boundaryKey,
|
||||
child: Container(
|
||||
width: screen.screenWidth - 60,
|
||||
height: (screen.screenWidth - 60) * 1.6,
|
||||
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 20),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage("share_bg.webp".mineImgPath),
|
||||
fit: BoxFit.fill)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
30.sizeBoxH,
|
||||
Consumer<GlobalStore>(
|
||||
builder: (_, provider, __) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
border:
|
||||
Border.all(color: Color(0x4DF68804), width: 3),
|
||||
),
|
||||
child: NetworkImageLoader(
|
||||
imageUrl: provider.meInfo?.portrait ?? '',
|
||||
width: 82,
|
||||
height: 82,
|
||||
borderRadius: 50,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
10.sizeBoxH,
|
||||
Text(
|
||||
'我的邀请码',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .9),
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
10.sizeBoxH,
|
||||
Text(
|
||||
globalStore.meInfo?.promotionCode ?? "",
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .55),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text("每邀请3人,送3天VIP",
|
||||
style:
|
||||
TextStyle(fontSize: 14, color: Color(0xFFF68804))),
|
||||
10.sizeBoxH,
|
||||
Row(
|
||||
children: [
|
||||
Spacer(),
|
||||
Center(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(11),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage("code_bg.webp".mineImgPath),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(6),
|
||||
color: Colors.white,
|
||||
child: QrImageView(
|
||||
data: globalStore.meInfo?.promoteURL ?? "",
|
||||
version: QrVersions.auto,
|
||||
size: 100,
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
],
|
||||
),
|
||||
10.sizeBoxH,
|
||||
Text(
|
||||
'提示*苹果手机请用相机扫码/安卓手机\n推荐UC浏览器扫码',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .55),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
10.sizeBoxH,
|
||||
Container(
|
||||
height: 30,
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 18, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: 0),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
child: Text(
|
||||
'${Config.appName} 官网地址 ${Address.groundUrl ?? ""}',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: .55),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
12.sizeBoxH,
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
20.sizeBoxH,
|
||||
InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () async {
|
||||
final ok = await ImageUtil.saveWidgetToAlbum(boundaryKey);
|
||||
showToast(ok ? "保存成功" : "保存失败,请重试");
|
||||
},
|
||||
child: Container(
|
||||
height: 44,
|
||||
width: 220,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
color: Color(0xFFF68804)),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
"保存图片",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: globalStore.meInfo?.promoteURL ?? ""));
|
||||
showToast('复制成功');
|
||||
},
|
||||
child: Text(
|
||||
"复制链接",
|
||||
style: TextStyle(
|
||||
color: Color(0x8CFFFFFF),
|
||||
decoration: TextDecoration.underline, // 添加下划线
|
||||
decorationColor: Color(0x8CFFFFFF), // 下划线颜色
|
||||
decorationThickness: 1.0, // 下划线粗细
|
||||
decorationStyle: TextDecorationStyle.solid, // 下划线样式
|
||||
),
|
||||
))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hgdj/tools_base/widget/net_image_widget.dart';
|
||||
|
||||
import '../../../hj_model/home/collection_model.dart';
|
||||
|
||||
class SimpleCollectionsItem extends StatelessWidget {
|
||||
final CollectionModel videoModel;
|
||||
const SimpleCollectionsItem(this.videoModel, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(bottom: 14),
|
||||
child: NetworkImageLoader(
|
||||
imageUrl: videoModel.cover ?? '',
|
||||
height: 222,
|
||||
borderRadius: 12,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/hj_model/splash/domain_source_model.dart';
|
||||
import 'package:hgdj/hj_page/main_page/provider/msg_provider.dart';
|
||||
import 'package:hgdj/hj_utils/date_time_util.dart';
|
||||
import 'package:hgdj/tools_base/widget/net_image_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../tools_base/global_store/store.dart';
|
||||
import '../mine_vip/mine_charge_vip_page.dart';
|
||||
|
||||
//会员卡入口
|
||||
class VipEntryCard extends StatelessWidget {
|
||||
const VipEntryCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 分层配置刷新时重建(取最新 meTab)
|
||||
return Consumer<MineMsgProvider>(
|
||||
builder: (context, _, __) {
|
||||
final config = MineMsgProvider().payTier?.config;
|
||||
// 分层图优先:有 meTab 后端整图就用分层卡(含 tick 倒计时,点击带 vipCard)
|
||||
if ((config?.meTab ?? '').isNotEmpty) return _layeredCard(config!);
|
||||
// 否则走原三态(本地图 + 会员/新人/默认)
|
||||
return _defaultCard();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _defaultCard() {
|
||||
return GestureDetector(
|
||||
onTap: () => Get.to(() => MineChargeVipPage()),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Stack(
|
||||
alignment: Alignment.centerLeft,
|
||||
children: [
|
||||
Image.asset(
|
||||
'mine_vip_entry_bg.webp'.mineImgPath,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.fill,
|
||||
height: 64,
|
||||
),
|
||||
Consumer2<GlobalStore, MineMsgProvider>(
|
||||
builder: (context, global, provider, child) {
|
||||
final isUpgrade =
|
||||
global.isVIP && global.meInfo?.isUpgrade == true;
|
||||
if (global.isVIP) return _vipRow(global, isUpgrade: isUpgrade);
|
||||
// 倒计时态/默认态的切换跟着 tick 走,过期当帧即收起,不必等分层配置刷新回来
|
||||
return ValueListenableBuilder<int>(
|
||||
valueListenable: provider.tick,
|
||||
builder: (_, __, ___) {
|
||||
final cd = provider.countdownConfig;
|
||||
if (cd != null) return _newerRow(cd);
|
||||
return _titleRow(
|
||||
title: '开通会员 享专属特权',
|
||||
subtitle: '解锁专属 畅享无限',
|
||||
buttonText: '开通会员');
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 支付分层会员卡:后端 meTab 整图 + lastDiscountTime 倒计时块,点击跳会员中心带 vipCard
|
||||
/// 按 343x64 设计稿搭建,FittedBox 整体等比缩放,倒计时块坐标即设计像素
|
||||
Widget _layeredCard(PayTierConfig config) {
|
||||
return GestureDetector(
|
||||
onTap: () => Get.to(() => MineChargeVipPage(vipID: config.vipCard)),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 343 / 64,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.fill,
|
||||
child: SizedBox(
|
||||
width: 343,
|
||||
height: 64,
|
||||
child: Stack(
|
||||
children: [
|
||||
NetworkImageLoader(
|
||||
imageUrl: config.meTab ?? "",
|
||||
width: 343,
|
||||
height: 64,
|
||||
fit: BoxFit.fill,
|
||||
borderRadius: 0),
|
||||
// 倒计时块监听 tick 每秒刷新(过期自动收起)
|
||||
ValueListenableBuilder<int>(
|
||||
valueListenable: MineMsgProvider().tick,
|
||||
builder: (_, __, ___) => config.hasDiscountCountdown
|
||||
? _layeredCountdown(config)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
//分层倒计时块(深色款,坐标按后端 meTab 图设计,不对就调 left/top)
|
||||
Widget _layeredCountdown(PayTierConfig config) {
|
||||
return Positioned(
|
||||
left: 69,
|
||||
top: 37,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_layeredCell(config.discountHour),
|
||||
_layeredColon(),
|
||||
_layeredCell(config.discountMin),
|
||||
_layeredColon(),
|
||||
_layeredCell(config.discountSec),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _layeredCell(String value) {
|
||||
return Container(
|
||||
width: 22,
|
||||
height: 20,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xff1B1B1B),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
border: Border.all(color: const Color(0xffFFE381), width: 0.5),
|
||||
),
|
||||
child: Text(value,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 12)),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _layeredColon() => const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Text(":", style: TextStyle(color: Colors.white, fontSize: 12)),
|
||||
);
|
||||
|
||||
Widget _newerRow(PayTierConfig config) {
|
||||
return _cardRow(
|
||||
left: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
_goldText('新用户升级会员特惠', 16),
|
||||
_countdown(config),
|
||||
]),
|
||||
buttonText: '开通会员',
|
||||
);
|
||||
}
|
||||
|
||||
Widget _vipRow(GlobalStore global, {bool isUpgrade = false}) {
|
||||
String vipTitle = global.meInfo?.vipName ?? '';
|
||||
if (vipTitle.isEmpty) {
|
||||
final level = global.meInfo?.vipLevel ?? 0;
|
||||
if (level == 1) vipTitle = '普通卡';
|
||||
if (level == 2) vipTitle = 'SVIP卡';
|
||||
if (level >= 3) vipTitle = '至尊永久卡';
|
||||
}
|
||||
return _titleRow(
|
||||
title: '$vipTitle会员',
|
||||
subtitle:
|
||||
'到期时间:${DateTimeUtil.utcTurnYear(global.meInfo?.vipExpireDate, char: ".")}',
|
||||
buttonText: isUpgrade ? '补差价升级' : '会员中心',
|
||||
onButtonTap: isUpgrade ? () => Get.to(() => MineChargeVipPage()) : null,
|
||||
);
|
||||
}
|
||||
|
||||
//通用布局:标题+副标题+按钮
|
||||
Widget _titleRow(
|
||||
{required String title,
|
||||
required String subtitle,
|
||||
required String buttonText,
|
||||
VoidCallback? onButtonTap}) {
|
||||
return _cardRow(
|
||||
left: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
_goldText(title, 16),
|
||||
Text(subtitle,
|
||||
style: TextStyle(fontSize: 10, color: Color(0xFFE9E8E7))),
|
||||
]),
|
||||
buttonText: buttonText,
|
||||
onButtonTap: onButtonTap,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _cardRow(
|
||||
{required Widget left,
|
||||
required String buttonText,
|
||||
VoidCallback? onButtonTap}) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 60, right: 16),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: left),
|
||||
_button(buttonText, onTap: onButtonTap),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _button(String text, {VoidCallback? onTap}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior:
|
||||
onTap != null ? HitTestBehavior.opaque : HitTestBehavior.translucent,
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
height: 26,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xffFBE9BC), Color(0xffFFFFFF), Color(0xffFBE9BC)],
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Color(0xFF000000),
|
||||
fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
//渐变金色文字
|
||||
Widget _goldText(String text, double fontSize) {
|
||||
return ShaderMask(
|
||||
shaderCallback: (bounds) => LinearGradient(
|
||||
colors: [Color(0xffFAE6D2), Color(0xffFFFFFF)],
|
||||
).createShader(bounds),
|
||||
blendMode: BlendMode.srcIn,
|
||||
child: Text(text,
|
||||
style: TextStyle(fontSize: fontSize, fontWeight: FontWeight.w600)),
|
||||
);
|
||||
}
|
||||
|
||||
//倒计时(外层 _defaultCard 已监听 tick,逐秒重建)
|
||||
Widget _countdown(PayTierConfig config) {
|
||||
return ShaderMask(
|
||||
shaderCallback: (bounds) => LinearGradient(
|
||||
colors: [Color(0xffE9E8E7), Color(0xffE9E8E7)],
|
||||
).createShader(bounds),
|
||||
blendMode: BlendMode.srcIn,
|
||||
child: Row(children: [
|
||||
Icon(Icons.access_time, size: 14),
|
||||
_timeUnit(config.discountHour),
|
||||
_timeColon(),
|
||||
_timeUnit(config.discountMin),
|
||||
_timeColon(),
|
||||
_timeUnit(config.discountSec),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _timeUnit(String val) => Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 2),
|
||||
child:
|
||||
Text(val, style: TextStyle(fontSize: 12, color: Color(0xFFFF844D))),
|
||||
);
|
||||
|
||||
Widget _timeColon() =>
|
||||
Text(":", style: TextStyle(fontSize: 10, color: Color(0xFFFF844D)));
|
||||
}
|
||||
Reference in New Issue
Block a user