初始化

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,29 @@
class ApcApiModel {
String? bank;
bool? validated;
String? cardType;
String? key;
List<dynamic>? messages;
String? stat;
static ApcApiModel? fromMap(Map<String, dynamic>? map) {
if (map == null) return null;
ApcApiModel apcApiModel = ApcApiModel();
apcApiModel.bank = map['bank'];
apcApiModel.validated = map['validated'];
apcApiModel.cardType = map['cardType'];
apcApiModel.key = map['key'];
apcApiModel.messages = map['messages'];
apcApiModel.stat = map['stat'];
return apcApiModel;
}
Map toJson() => {
"bank": bank,
"validated": validated,
"cardType": cardType,
"key": key,
"messages": messages,
"stat": stat,
};
}
@@ -0,0 +1,123 @@
class WithdrawConfig {
WithdrawConfig({
this.channels,
this.id,
this.cashTax,
this.coinTax,
this.gameTax,
});
List<Channel>? channels;
int? id;
int? cashTax;
int? coinTax;
int? gameTax;
Channel? get getBankCardChannel {
for (Channel item in (channels ?? [])) {
if (item.isBankCard) {
return item;
}
}
return null;
}
List<Channel> get realChannel {
final channel_ = <Channel>[];
if (getBankCardChannel != null) {
channel_.add(getBankCardChannel!);
}
if (getUsdtChannel != null) {
channel_.add(getUsdtChannel!);
}
return channel_;
}
Channel? get getUsdtChannel {
for (Channel item in (channels ?? [])) {
if (item.isUsdt) {
return item;
}
}
return null;
}
bool get hasBankCard {
for (Channel item in (channels ?? [])) {
if (item.isBankCard) {
return true;
}
}
return false;
}
bool get hasUsdt {
for (Channel item in (channels ?? [])) {
if (item.isUsdt) {
return true;
}
}
return false;
}
factory WithdrawConfig.fromJson(Map<String, dynamic> json) => WithdrawConfig(
channels: List<Channel>.from(json["channels"].map((x) => Channel.fromJson(x))),
id: json["ID"],
cashTax: json["cashTax"],
coinTax: json["coinTax"],
gameTax: json["gameTax"],
);
Map<String, dynamic> toJson() => {
"channels": channels == null ? [] : List<dynamic>.from(channels!.map((x) => x.toJson())),
"ID": id,
"cashTax": cashTax,
"coinTax": coinTax,
"gameTax": gameTax,
};
}
class Channel {
Channel({
this.channelName,
this.cid,
this.payType,
this.minMoney,
this.maxMoney,
this.qpMinMoney,
this.qpMaxMoney,
});
String? channelName;
String? cid;
String? payType;
int? minMoney;
int? maxMoney;
int? qpMinMoney;
int? qpMaxMoney;
bool get isBankCard => payType?.toLowerCase() == "bankcard";
bool get isUsdt => payType?.toLowerCase() == "usdt";
factory Channel.fromJson(Map<String, dynamic> json) => Channel(
channelName: json["channelName"],
cid: json["cid"],
payType: json["payType"],
minMoney: json["minMoney"],
maxMoney: json["maxMoney"],
qpMinMoney: json["qpMinMoney"],
qpMaxMoney: json["qpMaxMoney"],
);
Map<String, dynamic> toJson() => {
"channelName": channelName,
"cid": cid,
"payType": payType,
"minMoney": minMoney,
"maxMoney": maxMoney,
"qpMinMoney": qpMinMoney,
"qpMaxMoney": qpMaxMoney,
};
}
@@ -0,0 +1,160 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hgdj/hj_utils/api_service/mine_service.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'mine_withdrawal_record_page.dart';
import 'widget/record_list_item.dart';
import 'withdraw_details_model.dart';
abstract class MineWithdrawalRecordLogic extends GetxController {
RefreshController? refreshController;
int page = 1;
WithdrawDetailsModel? withdrawDetailsModel;
final dataSource = [];
bool isLoading = true;
@override
onReady() {
super.onReady();
fetchPageData();
}
@mustCallSuper
fetchPageData({bool isRefresh = true}) async {
if (isRefresh) page = 1;
}
Widget instanceChildItem(int index);
}
//提现明细
class WithDrawalRecordController extends MineWithdrawalRecordLogic {
@override
fetchPageData({bool isRefresh = true}) async {
super.fetchPageData(isRefresh: isRefresh);
final result =
await MineService.getWithdrawDetails(pageNumber: page, pageSize: 10);
isLoading = false;
if (isRefresh) {
dataSource.clear();
refreshController?.refreshCompleted();
}
result?.hasNext ?? false
? refreshController?.loadComplete()
: refreshController?.loadNoData();
dataSource.addAll(result?.list ?? []);
page += 1;
update();
}
@override
Widget instanceChildItem(int index) {
return WithdrawalRecordItem(dataSource[index]);
}
}
//金币订单明细
class GoldBillRecordController extends MineWithdrawalRecordLogic {
@override
fetchPageData({bool isRefresh = true}) async {
super.fetchPageData(isRefresh: isRefresh);
final res = await MineService.getBillData(
pageSize: 15,
pageNumber: page,
type: 1,
);
isLoading = false;
if (isRefresh) {
dataSource.clear();
refreshController?.refreshCompleted();
}
res?.hasNext ?? false
? refreshController?.loadComplete()
: refreshController?.loadNoData();
dataSource.addAll(res?.list ?? []);
page += 1;
update();
}
@override
Widget instanceChildItem(int index) {
return GoldRecordItem(dataSource[index]);
}
}
//充值明细
class RechargeRecordController extends MineWithdrawalRecordLogic {
@override
fetchPageData({bool isRefresh = true}) async {
super.fetchPageData(isRefresh: isRefresh);
final res =
await MineService.getRechargeBill(pageNumber: page, pageSize: 15);
isLoading = false;
if (isRefresh) {
dataSource.clear();
refreshController?.refreshCompleted();
}
res?.hasNext ?? false
? refreshController?.loadComplete()
: refreshController?.loadNoData();
dataSource.addAll(res?.list ?? []);
page += 1;
update();
}
@override
Widget instanceChildItem(int index) {
return Padding(
padding: EdgeInsets.only(bottom: 14),
child: RechargeRecordItem(dataSource[index]),
);
}
}
//收益明细
class IncomeRecordController extends MineWithdrawalRecordLogic {
@override
fetchPageData({bool isRefresh = true}) async {
super.fetchPageData(isRefresh: isRefresh);
final res =
await MineService.getIncomeRecord(pageNumber: page, pageSize: 15);
isLoading = false;
if (isRefresh) {
dataSource.clear();
refreshController?.refreshCompleted();
}
res?.hasNext ?? false
? refreshController?.loadComplete()
: refreshController?.loadNoData();
dataSource.addAll(res?.list ?? []);
page += 1;
update();
}
@override
Widget instanceChildItem(int index) {
return Padding(
padding: EdgeInsets.only(bottom: 12),
child: InComeRecordItem(dataSource[index]),
);
}
}
MineWithdrawalRecordLogic instanceController(RecordType type) {
switch (type) {
case RecordType.bill:
return GoldBillRecordController();
case RecordType.withdraw:
return WithDrawalRecordController();
case RecordType.recharge:
return RechargeRecordController();
case RecordType.income:
return IncomeRecordController();
default:
throw '$type 没有找到';
}
}
@@ -0,0 +1,64 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:hgdj/tools_base/loading/loading_center_widget.dart';
import 'package:hgdj/tools_base/refresh/pull_refresh.dart';
import 'mine_withdrawal_record_logic.dart';
enum RecordType {
recharge('充值记录'),
withdraw('提现明细'),
bill('余额明细'),
income('业绩明细');
final String title;
const RecordType(this.title);
}
//明细综合页面
class RecordsPage extends StatefulWidget {
final RecordType type; // 0:收益, 1 提现
const RecordsPage(this.type, {super.key});
@override
State<RecordsPage> createState() => _RecordsPageState();
}
class _RecordsPageState extends State<RecordsPage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return GetBuilder<MineWithdrawalRecordLogic>(
init: instanceController(widget.type),
builder: (controller) {
return Scaffold(
appBar: AppBar(title: Text(widget.type.title)),
body: pullYsRefresh(
onRefresh: (refreshController) => controller.fetchPageData(),
onLoading: (refreshController) =>
controller.fetchPageData(isRefresh: false),
onInit: (ctr) => controller.refreshController = ctr,
child: () {
if (controller.isLoading) return LoadingCenterWidget();
if (controller.dataSource.isEmpty)
return CErrorWidget(
retryOnTap: () => controller.fetchPageData());
return ListView.builder(
itemCount: controller.dataSource.length,
padding: EdgeInsets.only(top: 12, left: 16.w, right: 16.w),
itemBuilder: (context, index) =>
controller.instanceChildItem(index),
);
}(),
),
);
},
);
}
}
@@ -0,0 +1,364 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hgdj/assets_tool/app_colors.dart';
import 'package:hgdj/assets_tool/images.dart';
import 'package:hgdj/extension/extensions.dart';
import 'package:hgdj/hj_model/mine/exchange/bill_item_model.dart';
import 'package:hgdj/hj_utils/date_time_util.dart';
import 'package:hgdj/hj_utils/screen.dart';
import 'package:hgdj/tools_base/toast.dart';
import '../withdraw_details_model.dart';
class GoldRecordItem extends StatelessWidget {
final BillItemModel model;
const GoldRecordItem(this.model, {super.key});
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.sizeBoxH,
Text(
model.tranType ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w400),
),
4.sizeBoxH,
Text(
model.desc ?? '',
style: TextStyle(
color: Colors.white.withValues(alpha: .5),
fontSize: 12,
fontWeight: FontWeight.w400),
),
4.sizeBoxH,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
model.createdAt.utcToYMDHMS(),
style: TextStyle(
color: Colors.white.withValues(alpha: .5),
fontSize: 12,
fontWeight: FontWeight.w400),
),
Text(
'${model.realCount}${model.unit}',
style: const TextStyle(
color: Color(0xffF68804),
fontSize: 14,
fontWeight: FontWeight.w400),
),
],
),
12.sizeBoxH,
1.line,
],
),
),
],
);
}
}
//充值记录
class RechargeRecordItem extends StatelessWidget {
final ListBean model;
const RechargeRecordItem(this.model, {super.key});
@override
Widget build(BuildContext context) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"${model.productName ?? ""}",
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
2.sizeBoxH,
InkWell(
enableFeedback: false,
onTap: () {
///复制到剪切板
Clipboard.setData(ClipboardData(text: model.orderId ?? ''));
showToast('复制成功');
},
child: Row(
children: [
Flexible(
child: Text(
'账单编号' + ': ${model.orderId}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
color: Color(0xE5FFFFFF),
fontWeight: FontWeight.w500),
),
),
2.sizeBoxW,
Image.asset(
'icon_copy.png'.mineImgPath,
width: 24,
),
],
),
),
2.sizeBoxH,
Text(
"状态: ${getStatus(model.status ?? 0)}",
style: TextStyle(
color: Color(0xffF68804),
fontSize: 12,
),
),
2.sizeBoxH,
Text(
model.createdAt?.utcToYMD() ?? "",
style: TextStyle(fontSize: 12, color: Color(0xff525252)),
),
12.sizeBoxH,
0.5.line,
],
),
);
}
///支付状态
String getStatus(int status) {
switch (status) {
case 1:
return "进行中";
case 2:
return "购买失败";
case 3:
return "购买成功";
}
return "未知";
}
Color getStatusColor(int status) {
var statusStr = Color(0xffffd382);
switch (status) {
case 2:
statusStr = Color(0xffFF1060);
break;
case 3:
statusStr = Color(0xff28C445);
break;
}
return statusStr;
}
}
class InComeRecordItem extends StatelessWidget {
final IncomeModel model;
const InComeRecordItem(this.model, {super.key});
@override
Widget build(BuildContext context) {
return Container(
child: Column(children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${model.tranType}",
style: const TextStyle(
color: Color(0xFFEFEFEF),
fontWeight: FontWeight.w500,
fontSize: 14.0),
),
Container(
margin: EdgeInsets.symmetric(vertical: 4),
child: Text(
model.desc ?? "",
style: TextStyle(fontSize: 12, color: Colors.white60),
),
),
Text(
DateTimeUtil.utc2iso(model.createdAt ?? ''),
style: const TextStyle(
color: Color(0xFF525252),
fontWeight: FontWeight.w400,
fontSize: 12.0),
),
12.sizeBoxH,
],
),
),
12.sizeBoxW,
Text(
model.tranTypeInt == 111
? "+${model.actualAmount}"
: "+${model.actualAmount}金币",
style: const TextStyle(
color: const Color(0xFFF68804),
fontWeight: FontWeight.w400,
fontSize: 12.0),
textAlign: TextAlign.left)
],
),
0.5.line,
]));
}
}
//提现明细cell
class WithdrawalRecordItem extends StatefulWidget {
final ListBean model;
const WithdrawalRecordItem(this.model, {super.key});
@override
State<WithdrawalRecordItem> createState() => _WithdrawalRecordItemState();
}
class _WithdrawalRecordItemState extends State<WithdrawalRecordItem> {
ListBean get model => widget.model;
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.sizeBoxH,
Text(
'${(model.money ?? 0) ~/ 100}',
style: TextStyle(
fontSize: 14,
color: AppColors.actionRed,
fontWeight: FontWeight.w500),
),
6.sizeBoxH,
Row(
children: [
Expanded(
child: Text(
'账单编号' + ': ${model.id}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
color: Color(0xE5FFFFFF),
fontWeight: FontWeight.w500),
),
),
InkWell(
enableFeedback: false,
onTap: () {
///复制到剪切板
Clipboard.setData(ClipboardData(text: model.id ?? ''));
showToast('复制成功');
},
child: Image.asset(
'mine_copy.png'.mineImgPath,
width: 24,
),
)
],
),
8.sizeBoxH,
Text(
'${getPayType(model.payType ?? "")}${getStatus(model.status ?? 0)}',
style: TextStyle(fontSize: 12, color: getStatusColor()),
),
6.sizeBoxH,
Row(
children: [
Text(
DateTimeUtil.utc2iso(model.createdAt ?? ''),
style: TextStyle(fontSize: 12, color: Color(0x8CFFFFFF)),
),
Spacer(),
if (model.status != 5 && model.status != 1) ...[
InkWell(
enableFeedback: false,
onTap: () => setState(() {
model.showReason = !model.showReason;
}),
child: Text(
'查看原因',
style: TextStyle(fontSize: 12, color: AppColors.actionRed),
),
)
],
],
),
if (model.showReason) ...[
12.sizeBoxH,
Text(
'${model.statusDesc}',
style: TextStyle(fontSize: 12, color: Colors.white),
),
],
12.sizeBoxH,
0.5.line,
],
),
);
}
String getPayType(String payType) {
if (payType.endsWith("alipay")) {
return "支付宝";
} else if (payType.endsWith("usdt")) {
return "USDT";
} else {
return "银行卡";
}
}
///支付状态
String getStatus(int status) {
switch (status) {
case 1:
return '提现审核中';
case 2:
return '审核通过,转账中';
case 3:
return '提现已拒绝';
case 4:
return '未知错误';
case 5:
return '提现成功';
case 6:
return '提现失败';
}
return '';
}
///支付状态
Color getStatusColor() {
if (model.status == 5) {
return Color(0xff0360FC);
} else {
return Color(0x8CFFFFFF);
}
}
}
@@ -0,0 +1,369 @@
/// hasNext : true
class WithdrawDetailsModel {
bool? hasNext;
List<ListBean>? list;
List<ResultBean>? result;
int? total;
static WithdrawDetailsModel? fromJson(Map<String, dynamic> map) {
WithdrawDetailsModel withdrawDetailsModel = WithdrawDetailsModel();
withdrawDetailsModel.hasNext = map['hasNext'];
withdrawDetailsModel.list = []..addAll((map['list'] as List? ?? []).map((o) => ListBean.fromMap(o)));
withdrawDetailsModel.result = []..addAll((map['result'] as List? ?? []).map((o) => ResultBean.fromMap(o)));
withdrawDetailsModel.total = map['total'];
return withdrawDetailsModel;
}
Map toJson() => {
"hasNext": hasNext,
"list": list,
"result": result,
"total": total,
};
}
class ResultBean {
int? uid;
int? amount;
int? money;
int? payMoney;
int? withdrawType;
int? status;
String? id;
String? name;
String? oid;
String? payType;
String? actName;
String? act;
String? userIp;
String? deviceType;
String? devID;
String? statusDesc;
String? checkedAt;
String? progressAt;
String? failureAt;
String? successAt;
String? updatedAt;
String? createdAt;
String? receivedAt;
static ResultBean fromMap(Map<String, dynamic> map) {
ResultBean info = ResultBean();
info.id = map['id'];
info.uid = map['uid'];
info.name = map['name'];
info.amount = map['amount'];
info.oid = map['oid'];
info.money = map['money'];
info.payMoney = map['payMoney'];
info.payType = map['payType'];
info.withdrawType = map['withdrawType'];
info.actName = map['actName'];
info.act = map['act'];
info.userIp = map['userIp'];
info.deviceType = map['deviceType'];
info.devID = map['devID'];
info.status = map['status'];
info.statusDesc = map['statusDesc'];
info.checkedAt = map['checkedAt'];
info.progressAt = map['progressAt'];
info.failureAt = map['failureAt'];
info.successAt = map['successAt'];
info.updatedAt = map['updatedAt'];
info.createdAt = map['createdAt'];
info.receivedAt = map['receivedAt'];
return info;
}
Map toJson() => {
"id": id,
"uid": uid,
"name": name,
"amount": amount,
"oid": oid,
"money": money,
"payMoney": payMoney,
"payType": payType,
"withdrawType": withdrawType,
"actName": actName,
"act": act,
"userIp": userIp,
"deviceType": deviceType,
"devID": devID,
"status": status,
"statusDesc": statusDesc,
"checkedAt": checkedAt,
"progressAt": progressAt,
"failureAt": failureAt,
"successAt": successAt,
"updatedAt": updatedAt,
"createdAt": createdAt,
"receivedAt": receivedAt,
};
}
class ListBean {
int? money;
int? payMoney;
int? uid;
int? amount;
int? withdrawType;
int? status;
String? id;
String? orderId;
String? name;
String? oid;
String? payType;
String? actName;
String? act;
String? userIp;
String? deviceType;
String? devID;
String? statusDesc;
String? checkedAt;
String? progressAt;
String? failureAt;
String? successAt;
String? updatedAt;
String? createdAt;
String? desc;
String? receivedAt;
String? productName;
double? actualAmount;
bool showReason = false; //本地添加字段,是否展示原因
static ListBean fromMap(Map<String, dynamic> map) {
ListBean info = ListBean();
info.id = map['id'];
info.orderId = map['orderId'];
info.uid = map['uid'];
info.name = map['name'];
info.amount = map['amount'];
info.oid = map['oid'];
info.money = map['money'];
info.payMoney = map['payMoney'];
info.payType = map['payType'];
info.withdrawType = map['withdrawType'];
info.actName = map['actName'];
info.act = map['act'];
info.userIp = map['userIp'];
info.deviceType = map['deviceType'];
info.devID = map['devID'];
info.status = map['status'];
info.statusDesc = map['statusDesc'];
info.checkedAt = map['checkedAt'];
info.progressAt = map['progressAt'];
info.failureAt = map['failureAt'];
info.successAt = map['successAt'];
info.updatedAt = map['updatedAt'];
info.createdAt = map['createdAt'];
info.receivedAt = map['receivedAt'];
info.desc = map['desc'];
info.actualAmount = map['actualAmount']?.toDouble() ?? .0;
info.productName = map['productName'];
return info;
}
Map toJson() => {
"id": id,
"uid": uid,
"name": name,
"amount": amount,
"oid": oid,
"money": money,
"payMoney": payMoney,
"payType": payType,
"withdrawType": withdrawType,
"actName": actName,
"act": act,
"userIp": userIp,
"deviceType": deviceType,
"devID": devID,
"status": status,
"statusDesc": statusDesc,
"checkedAt": checkedAt,
"progressAt": progressAt,
"failureAt": failureAt,
"successAt": successAt,
"updatedAt": updatedAt,
"createdAt": createdAt,
"receivedAt": receivedAt,
"desc": desc,
"actualAmount": actualAmount,
};
}
class IncomeModel {
String? id;
int? uid;
String? purchaseOrder;
String? productID;
num? amount;
num? integral;
String? realIntegral;
num? actualIntegral;
num? actualAmount;
num? tax;
num? taxAmount;
String? channelType;
String? tranType;
num? tranTypeInt;
num? performance;
num? rechargeId;
RechargeUser? rechargeUser;
String? desc;
String? createdAt;
String? sysType;
num? agentLevel;
num? vipLevel;
String? realAmount;
String? money;
String? wlRealAmount;
num? fruitCoin;
num? downloadCount;
num? fruitCoinBalance;
num? aiMateBalance;
String? districtCode;
String? promSeqe;
bool? isDirect;
String? discBindAt;
IncomeModel(
{this.id,
this.uid,
this.purchaseOrder,
this.productID,
this.amount,
this.integral,
this.realIntegral,
this.actualIntegral,
this.actualAmount,
this.tax,
this.taxAmount,
this.channelType,
this.tranType,
this.tranTypeInt,
this.performance,
this.rechargeId,
this.rechargeUser,
this.desc,
this.createdAt,
this.sysType,
this.agentLevel,
this.vipLevel,
this.realAmount,
this.money,
this.wlRealAmount,
this.fruitCoin,
this.downloadCount,
this.fruitCoinBalance,
this.aiMateBalance,
this.districtCode,
this.promSeqe,
this.isDirect,
this.discBindAt});
handelMoney() {
double price = (actualAmount ?? 0) / 10;
return price.toStringAsFixed(2);
}
IncomeModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
uid = json['uid'];
purchaseOrder = json['purchaseOrder'];
productID = json['productID'];
amount = json['amount'];
integral = json['integral'];
realIntegral = json['realIntegral'];
actualIntegral = json['actualIntegral'];
actualAmount = json['actualAmount'];
tax = json['tax'];
taxAmount = json['taxAmount'];
channelType = json['channelType'];
tranType = json['tranType'];
tranTypeInt = json['tranTypeInt'];
performance = json['performance'];
rechargeId = json['rechargeId'];
rechargeUser = json['rechargeUser'] != null ? new RechargeUser.fromJson(json['rechargeUser']) : null;
desc = json['desc'];
createdAt = json['createdAt'];
sysType = json['sysType'];
agentLevel = json['agentLevel'];
vipLevel = json['vipLevel'];
realAmount = json['realAmount'];
money = json['money'];
wlRealAmount = json['wlRealAmount'];
fruitCoin = json['fruitCoin'];
downloadCount = json['downloadCount'];
fruitCoinBalance = json['fruitCoinBalance'];
aiMateBalance = json['aiMateBalance'];
districtCode = json['districtCode'];
promSeqe = json['promSeqe'];
isDirect = json['isDirect'];
discBindAt = json['DiscBindAt'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['uid'] = this.uid;
data['purchaseOrder'] = this.purchaseOrder;
data['productID'] = this.productID;
data['amount'] = this.amount;
data['integral'] = this.integral;
data['realIntegral'] = this.realIntegral;
data['actualIntegral'] = this.actualIntegral;
data['actualAmount'] = this.actualAmount;
data['tax'] = this.tax;
data['taxAmount'] = this.taxAmount;
data['channelType'] = this.channelType;
data['tranType'] = this.tranType;
data['tranTypeInt'] = this.tranTypeInt;
data['performance'] = this.performance;
data['rechargeId'] = this.rechargeId;
if (this.rechargeUser != null) {
data['rechargeUser'] = this.rechargeUser!.toJson();
}
data['desc'] = this.desc;
data['createdAt'] = this.createdAt;
data['sysType'] = this.sysType;
data['agentLevel'] = this.agentLevel;
data['vipLevel'] = this.vipLevel;
data['realAmount'] = this.realAmount;
data['money'] = this.money;
data['wlRealAmount'] = this.wlRealAmount;
data['fruitCoin'] = this.fruitCoin;
data['downloadCount'] = this.downloadCount;
data['fruitCoinBalance'] = this.fruitCoinBalance;
data['aiMateBalance'] = this.aiMateBalance;
data['districtCode'] = this.districtCode;
data['promSeqe'] = this.promSeqe;
data['isDirect'] = this.isDirect;
data['DiscBindAt'] = this.discBindAt;
return data;
}
}
class RechargeUser {
int? uid;
String? name;
String? portrait;
RechargeUser({this.uid, this.name, this.portrait});
RechargeUser.fromJson(Map<String, dynamic> json) {
uid = json['uid'];
name = json['name'];
portrait = json['portrait'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['uid'] = this.uid;
data['name'] = this.name;
data['portrait'] = this.portrait;
return data;
}
}
@@ -0,0 +1,239 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hgdj/hj_model/user/wallet_model.dart';
import 'package:hgdj/hj_utils/api_service/mine_service.dart';
import 'package:hgdj/tools_base/global_store/store.dart';
import 'package:hgdj/tools_base/loading/loading_helper.dart';
import '../../../alert/mine/vip_level_dialog.dart';
import 'package:hgdj/tools_base/toast.dart';
import '../../../track_event_manager/device_service.dart';
import '../mine_profit/bank_card_home_page.dart';
import '../mine_profit/model/alipay_bank_list_model.dart';
import 'in_come_entity.dart';
class WithdrawalLogic extends GetxController with GetTickerProviderStateMixin {
WithdrawalLogic get to => Get.find<WithdrawalLogic>();
TextEditingController? moneyController;
TextEditingController? accountController;
FocusNode focusNode = FocusNode();
bool isShowLoading = false;
WalletModel? userIncomeModel;
WithdrawConfig? configData;
TextEditingController? nameController;
bool isLoading = true;
int withdrawType = 1; // 0支付宝 1银行卡
Channel? selectChannel;
num handlingFee = 0; //手续费
num actualAmount = 0; //实际到账金额
AccountInfoModel? bankModel;
int get minBankCardMoney {
return (configData?.getBankCardChannel?.minMoney ?? 0) ~/ 100;
}
int get minUsdtMoney {
return (configData?.getUsdtChannel?.minMoney ?? 0) ~/ 100;
}
@override
void onInit() {
super.onInit();
moneyController = TextEditingController();
nameController = TextEditingController();
accountController = TextEditingController();
}
@override
void onReady() {
super.onReady();
loadData(showLoading: true);
globalStore.refreshWallet();
}
void changeWithdrawType(String payType) {
// withdrawType = value;
selectChannel = configData?.channels
?.firstWhere((element) => element.payType == payType);
withdrawType = configData?.channels
?.indexWhere((e) => e.payType == selectChannel?.payType) ??
0;
update();
}
///计算提现手续费、实际到账金额
void calcWithdrawAmount(String withdrawAmount) {
if (withdrawAmount.isEmpty) {
handlingFee = 0;
actualAmount = 0;
} else {
num withdrawAmoutNum = num.parse(withdrawAmount);
int coinTax = configData?.coinTax ?? 0;
if (coinTax == 0) {
handlingFee = 0;
actualAmount = withdrawAmoutNum;
} else {
double value = coinTax / 100.0;
final res = (withdrawAmoutNum * double.parse(value.toStringAsFixed(2)))
.toStringAsFixed(2);
handlingFee = double.parse(res).floor();
actualAmount = withdrawAmoutNum - handlingFee;
}
}
update(['money']);
}
@override
void dispose() {
super.dispose();
nameController?.dispose();
moneyController?.dispose();
accountController?.dispose();
focusNode.dispose();
}
Future<void> loadData({
bool isRefresh = true,
bool showLoading = false,
}) async {
//获取支付宝或者银行卡的费率
_withdrawConfigReq();
}
///提现配置请求
void _withdrawConfigReq() async {
final configData = await MineService.withdrawConfig();
this.configData = configData;
isLoading = false;
// 优先银行卡,其次 usdt
selectChannel = configData?.channels
?.firstWhereOrNull((element) => element.payType == 'bankcard') ??
configData?.channels
?.firstWhereOrNull((element) => element.payType == 'usdt');
if (selectChannel == null) {
showToast('暂无可用提现通道');
this.configData = null;
} else {
// 必须同步 withdrawType,页面按 channels[withdrawType] 取值,否则会索引错位甚至越界
withdrawType = configData?.channels
?.indexWhere((e) => e.payType == selectChannel?.payType) ??
0;
}
update();
}
///提交提现
void submitWithdraw() async {
try {
if (!globalStore.isRechargeVIP) {
showVipLevelDialog("您还不是VIP,无法使用提现功能");
return;
}
if ("bankcard" == selectChannel?.payType) {
//检验银行卡信息
_commonWithdrawReq(true);
} else if ("alipay" == selectChannel?.payType ||
"usdt" == selectChannel?.payType) {
_commonWithdrawReq(false);
}
} catch (e) {
showToast("提现错误:$e");
}
}
///公用提现方法
void _commonWithdrawReq(bool isbankType) async {
Channel channel = configData!.channels![withdrawType];
String money = moneyController?.text.trim() ?? "";
String accountDesc = accountController?.text.trim() ?? "";
if (isbankType) {
accountDesc = bankModel?.act ?? "";
}
if (money.isEmpty) {
showToast("提现金额不能为空");
return;
}
if (isbankType && bankModel == null) {
showToast("请选择提现银行卡号");
return;
}
if (!isbankType && accountDesc.isEmpty) {
showToast("请输入钱包地址");
return;
}
double incomeMoneyYuan = (userIncomeModel?.balance ?? 0) / 10;
int withdrawMoneyYuan = int.parse(money);
if (withdrawMoneyYuan > incomeMoneyYuan) {
showToast("提现金额不能大于余额");
return;
}
int minMoneyFen = configData?.channels![withdrawType].minMoney ?? 0;
double minMoneyYuan = minMoneyFen / 100;
if (withdrawMoneyYuan < minMoneyYuan) {
showToast("单笔提现金额不小于$minMoneyYuan元");
return;
}
int maxMoneyFen = configData?.channels![withdrawType].maxMoney ?? 0;
double maxMoneyYuan = maxMoneyFen / 100;
if (withdrawMoneyYuan > maxMoneyYuan) {
showToast("单笔提现金额不大于$maxMoneyYuan元");
return;
}
LoadingHelper.showLoading();
String deviceId = DeviceInfoService.deviceId;
String payType = channel.payType ?? "";
//payType 提现方式,alipaybankcardusdt
//money 提现金额
//name 用户名
//withdrawType 提现类型,0,代理提现; 1,金币提现
//actName 交易账户持有人
//act 交易账户
//devID 设备id
//productType 产品类型 0站群 1棋牌
var result = await MineService.withdraw(
payType,
accountDesc,
withdrawMoneyYuan * 100,
globalStore.meInfo?.name ?? "",
bankModel?.actName,
deviceId,
bankModel?.bankCode,
1,
0,
);
LoadingHelper.dismissLoading();
if (result == true) {
globalStore.refreshWallet(refresh: true);
showToast("提现提交成功~");
clearInputData();
} else {
showToast("提现失败~");
}
}
void gotoBankList() async {
var ret = await Get.to(
BankCardHomePage(selectModel: bankModel),
preventDuplicates: false,
);
if (ret is AccountInfoModel) {
bankModel = ret;
update();
}
}
clearInputData() {
nameController?.clear();
accountController?.clear();
moneyController?.clear();
}
}
@@ -0,0 +1,516 @@
import 'package:easy_rich_text/easy_rich_text.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:hgdj/assets_tool/images.dart';
import 'package:hgdj/hj_page/mine/widgets/gradient_text.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 'package:provider/provider.dart';
import '../../../assets_tool/app_colors.dart';
import '../../../hj_utils/widget_util.dart';
import '../../../routers/jump_router.dart';
import 'in_come_entity.dart';
import 'mine_withdrawal_record_page.dart';
import 'withdrawal_logic.dart';
//我要提现页面
class WithdrawalPage extends StatelessWidget {
const WithdrawalPage({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<WithdrawalLogic>(
init: WithdrawalLogic(),
builder: (logic) {
return Scaffold(
appBar: AppBar(
title: Text(
'我要提现',
style: TextStyle(
color: Color(0xE5FFFFFF),
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
actions: <Widget>[
GestureDetector(
child: Text(
'明细',
style: TextStyle(color: Color(0x73FFFFFF), fontSize: 12),
),
onTap: () => Get.to(RecordsPage(RecordType.withdraw)),
),
16.w.sizeBoxW,
],
),
body: () {
if (logic.isLoading) return LoadingCenterWidget();
if (logic.configData == null) return CErrorWidget();
const measureStyle = TextStyle(fontSize: 14);
var textPainter = TextPainter(
text: TextSpan(
text: '',
style: measureStyle,
),
textDirection: TextDirection.ltr,
textWidthBasis: TextWidthBasis.longestLine,
)..layout();
return Column(
children: [
Expanded(
child: SingleChildScrollView(
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior.onDrag,
child: Container(
margin: EdgeInsets.only(left: 16.w, right: 16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin:
EdgeInsets.only(top: 12.w, bottom: 18.w),
padding: EdgeInsets.symmetric(vertical: 12.w),
decoration: BoxDecoration(
color: Color(0x0DFFFFFF),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Row(
children: [
Container(
height: 22,
width: 8,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(11),
bottomRight: Radius.circular(11),
),
color: AppColors.actionRed,
),
),
12.sizeBoxW,
Text(
"余额(元)",
style: const TextStyle(
color: AppColors.actionRed,
fontWeight: FontWeight.w400,
fontSize: 14.0,
),
),
],
),
12.sizeBoxH,
Padding(
padding: EdgeInsets.only(left: 20),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Image.asset(
'coin_icon.webp'.mineImgPath,
width: 30),
4.sizeBoxW,
Consumer<GlobalStore>(
builder: (_, store, __) {
logic.userIncomeModel =
store.wallet;
return GradientText(
((store.wallet?.balance ?? 0) *
10 /
100)
.toStringAsFixed(2),
gradient: LinearGradient(
colors: [
Color(0xffFFE8BE),
Color(0xffE6B764)
],
tileMode: TileMode.mirror,
),
style: TextStyle(
fontSize: 31.5,
fontWeight: FontWeight
.w500), //目前fontSize设置为31.5 设置为32,显示不完全,存在系统bug
);
}),
],
),
),
],
)),
Row(
children: [
Text(
'提现币类:',
style: textStyle(
14, Color(0xE5FFFFFF), FontWeight.w500),
),
15.sizeBoxW,
Text(
'人民币',
style: textStyle(
14, Color(0x8CFFFFFF), FontWeight.w400),
),
],
),
16.sizeBoxH,
Row(
children: [
Text(
"提现金额:",
style: textStyle(
14, Color(0xE5FFFFFF), FontWeight.w500),
),
10.sizeBoxW,
Expanded(
child: Container(
alignment: Alignment.centerLeft,
height: 40,
padding:
EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color:
Colors.white.withValues(alpha: .05),
borderRadius: BorderRadius.circular(6),
),
child: TextField(
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp("[0-9]")),
LengthLimitingTextInputFormatter(9),
],
controller: logic.moneyController,
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w400),
cursorColor:
Colors.white.withValues(alpha: 0.7),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding:
EdgeInsets.only(bottom: 8),
hintStyle: TextStyle(
color: Color(0xFF525252),
fontSize: 12,
fontWeight: FontWeight.w400),
hintText:
'单笔提现金额范围 ${_getPayMoneyRange(logic)}', //"您目前
),
focusNode: logic.focusNode,
onChanged: (value) =>
logic.calcWithdrawAmount(value)),
),
)
],
),
16.sizeBoxH,
Row(
children: [
Text(
"提现方式:",
style: textStyle(
14, Color(0xE5FFFFFF), FontWeight.w500),
),
16.sizeBoxW,
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
height: 30,
child: Row(
children: logic.configData?.realChannel
.asMap()
.map((index, e) => MapEntry(
index,
_buildPayTypeUI(
e, logic, index)))
.values
.toList() ??
[],
),
),
),
),
],
),
16.sizeBoxH,
Row(
children: [
if (_isUsdt(logic)) ...[
Text(
'USDT地址:',
style: textStyle(
14, Color(0xE5FFFFFF), FontWeight.w500),
),
10.sizeBoxW,
Expanded(
child: Container(
decoration: BoxDecoration(
color:
Colors.white.withValues(alpha: .05),
borderRadius: BorderRadius.circular(6),
),
child: TextField(
textAlignVertical:
TextAlignVertical.center,
controller: logic.accountController,
maxLines: 1,
decoration: InputDecoration(
border: InputBorder.none,
hintText: _getHintText(logic),
hintStyle: TextStyle(
color: Color(0xff999999),
fontSize: 14,
fontWeight: FontWeight.w400),
isCollapsed: true,
contentPadding: EdgeInsets.symmetric(
horizontal: 12,
vertical:
(32 - textPainter.height) / 2,
),
),
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
),
)
] else ...[
Text(
'银行卡号:',
style: textStyle(
14, Color(0xE5FFFFFF), FontWeight.w500),
),
10.sizeBoxW,
Expanded(
child: InkWell(
enableFeedback: false,
onTap: () => logic.gotoBankList(),
child: Container(
alignment: Alignment.centerLeft,
height: 37,
padding: EdgeInsets.only(left: 10),
decoration: BoxDecoration(
color:
Colors.white.withValues(alpha: .05),
borderRadius: BorderRadius.circular(6),
),
child: Row(
children: [
Expanded(
child: Text(
logic.bankModel == null
? "请选择提现银行账号"
: "${logic.bankModel?.getBankName()}(${logic.bankModel?.act?.substring((logic.bankModel?.act?.length ?? 4) - 4)}) ${logic.bankModel?.actName}",
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
color: logic.bankModel == null
? Color(0xff525252)
: Colors.white,
),
),
),
6.sizeBoxW,
Icon(Icons.keyboard_arrow_right,
color: Colors.white, size: 16),
10.sizeBoxW,
],
),
),
)),
]
],
),
16.sizeBoxH,
GetBuilder<WithdrawalLogic>(
init: logic,
id: 'money',
builder: (controller) => EasyRichText(
'手续费率: ${logic.configData?.coinTax ?? 0}% 实际到账金额: ${logic.actualAmount}',
defaultStyle: textStyle(
14, Color(0xE5FFFFFF), FontWeight.w500),
patternList: [
EasyRichTextPattern(
targetString:
'${logic.configData?.coinTax ?? 0}%',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0x8CFFFFFF),
),
),
EasyRichTextPattern(
targetString:
'实际到账金额: ${logic.handlingFee}',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0x8CFFFFFF),
),
),
EasyRichTextPattern(
targetString: '${logic.actualAmount}',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0x8CFFFFFF),
),
),
],
),
),
18.sizeBoxH,
Text(
"提现规则:",
style: const TextStyle(
color: Color(0xE5FFFFFF),
fontWeight: FontWeight.w500,
fontSize: 16.0),
),
12.sizeBoxH,
Text(
"1、每次提现金额最低${(logic.configData?.channels == null ? 0 : logic.selectChannel?.minMoney ?? 0) ~/ 100}元起,"
"单笔提现最大${(logic.configData?.channels == null ? 0 : logic.selectChannel?.maxMoney ?? 0) ~/ 100}元,且为整数。\n"
"2、每次提现收取${logic.configData?.coinTax}%手续费。\n"
"3、仅支持银行卡提现,收款账户卡号与姓名一致,到账时间未72小时内。\n"
"4、申请提现后请随时关注收款账户进款通知,长时间未到账,请及时联系客服。\n",
style: const TextStyle(
color: Color(0x8CFFFFFF),
fontWeight: FontWeight.w400,
fontSize: 12.0,
height: 2),
),
30.sizeBoxH,
],
),
),
),
),
Column(
children: [
GestureDetector(
onTap: () => logic.submitWithdraw(),
child: Container(
height: 47,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: AppColors.actionRed,
borderRadius: BorderRadius.circular(3),
),
child: Text(
'立即提现',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500,
),
)),
),
10.sizeBoxH,
EasyRichText(
'提现中如有问题,请联系 在线客服',
defaultStyle:
textStyle(12, Colors.white, FontWeight.w400),
patternList: [
EasyRichTextPattern(
targetString: '在线客服',
style: TextStyle(color: Color(0xffFFD460)),
recognizer: TapGestureRecognizer()
..onTap = pushToCustomService,
),
],
),
34.sizeBoxH,
],
)
],
);
}());
},
);
}
///支付类型
Widget _buildPayTypeUI(Channel channel, WithdrawalLogic logic, int index) {
bool isSelected = logic.selectChannel?.payType == channel.payType;
return InkWell(
enableFeedback: false,
onTap: () => logic.changeWithdrawType(channel.payType ?? ''),
child: Row(
children: [
Image.asset(
channel.payType == 'usdt'
? 'ic_usdt.png'.mineImgPath
: 'ic_union.png'.mineImgPath,
width: 30),
4.sizeBoxW,
Text(_getPayTypeName(channel.payType ?? ""),
style: TextStyle(color: Colors.white, fontSize: 14)),
10.sizeBoxW,
Image.asset(
isSelected
? 'radio_sel.png'.commonImgPath
: 'mine_withdraw_nor.png'.mineImgPath,
width: 24,
),
24.sizeBoxW,
],
),
);
}
///获取支付方式名称
String _getPayTypeName(String payType) {
if ("alipay" == payType) {
return "支付宝";
} else if ("bankcard" == payType) {
return "银行卡";
} else if ("usdt" == payType) {
return "USDT";
}
return "银行卡";
}
///获取提现金额范围
String _getPayMoneyRange(WithdrawalLogic logic) {
final channel = _currentChannel(logic);
if (channel == null) return "0";
return "${(channel.minMoney ?? 0) / 100}-${(channel.maxMoney ?? 0) / 100}";
}
String _getHintText(WithdrawalLogic logic) {
final channel = _currentChannel(logic);
switch (channel?.payType ?? "") {
case 'alipay':
return '请输入支付宝账号';
case 'bankcard':
return '请输入银行卡号';
case 'usdt':
return '请输入USDT地址';
default:
return "";
}
}
bool _isUsdt(WithdrawalLogic logic) =>
(_currentChannel(logic)?.payType ?? "") == 'usdt';
// 按 withdrawType 取当前通道,带越界保护(配置异常时不崩溃)
Channel? _currentChannel(WithdrawalLogic logic) {
final channels = logic.configData?.channels ?? [];
final index = logic.withdrawType;
if (index < 0 || index >= channels.length) return null;
return channels[index];
}
}