初始化

This commit is contained in:
谢宇宁
2026-09-15 15:44:13 +07:00
commit a23ba685e9
1065 changed files with 101122 additions and 0 deletions
@@ -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)));
}