规则和历史

This commit is contained in:
2026-02-09 17:28:12 +08:00
parent 45e45d8160
commit 9cd87b0535
14 changed files with 395 additions and 18 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -14,7 +14,6 @@ class MallDetailController extends GetxController with BaseControllerMixin {
late final int goodsId;
final Rx<GoodsModel?> goodsDetail = Rx<GoodsModel?>(null);
final RxBool isLoading = true.obs;
final addressController = TextEditingController();
final nameController = TextEditingController();
@@ -33,8 +32,6 @@ class MallDetailController extends GetxController with BaseControllerMixin {
bool get listenLifecycleEvent => true;
Future<void> getGoodsDetail() async {
isLoading.value = true;
updateUi();
try {
var response = await HttpService.to.post(
'appointment/score/getScoreGoodsDetail',
@@ -57,7 +54,6 @@ class MallDetailController extends GetxController with BaseControllerMixin {
showErrorToast('网络异常,请稍后重试');
Get.back();
} finally {
isLoading.value = false;
updateUi();
}
}

View File

@@ -24,9 +24,7 @@ class MallDetailPage extends GetView<MallDetailController> {
onPressed: () => Get.back(),
),
),
body: controller.isLoading.value
? const Center(child: CircularProgressIndicator())
: GestureDetector(
body: GestureDetector(
onTap: () {
hideKeyboard();
},

View File

@@ -1,6 +1,9 @@
import 'package:getx_scaffold/getx_scaffold.dart';
import 'package:ln_jq_app/common/model/base_model.dart';
import 'package:ln_jq_app/pages/c_page/mall/detail/view.dart';
import 'package:ln_jq_app/pages/c_page/mall/orders/view.dart';
import 'package:ln_jq_app/pages/c_page/mall/rule/view.dart';
class GoodsModel {
final int id;
@@ -139,10 +142,19 @@ class MallController extends GetxController with BaseControllerMixin {
}
}
/// 兑换商品 (预留)
/// 兑换商品
void exchangeGoods(GoodsModel goods) {
Get.to(() => MallDetailPage(), arguments: {'goodsId': goods.id})?.then((val) {
refreshData();
});
Get.to(() => const MallDetailPage(), arguments: {'goodsId': goods.id})
?.then((_) => refreshData());
}
///规则说明
void toRuleDes() {
Get.to(() => const MallRulePage());
}
///历史订单
void toOrders() {
Get.to(() => const MallOrdersPage());
}
}

View File

@@ -117,7 +117,13 @@ class MallPage extends GetView<MallController> {
style: TextStyle(color: Colors.white70, fontSize: 14.sp),
),
const SizedBox(width: 4),
const Icon(Icons.help_outline, color: Colors.white70, size: 14),
GestureDetector(
onTap: (){
controller.toRuleDes();
},
child: const Icon(Icons.help_outline, color: Colors.white70, size: 14),
)
],
),
Text(
@@ -160,7 +166,9 @@ class MallPage extends GetView<MallController> {
),
),
TextButton(
onPressed: () {},
onPressed: () {
controller.toOrders();
},
child: Text(
'历史订单',
style: TextStyle(

View File

@@ -0,0 +1,94 @@
import 'dart:developer';
import 'package:get/get.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'package:ln_jq_app/common/model/base_model.dart';
class OrderModel {
final int id;
final int scoreGoodsId;
final String goodsName;
final String? goodsImage;
final String? goodsContent;
final String address;
final String createTime;
final String score;
OrderModel({
required this.id,
required this.scoreGoodsId,
required this.goodsName,
this.goodsImage,
this.goodsContent,
required this.address,
required this.createTime,
required this.score,
});
factory OrderModel.fromJson(Map<String, dynamic> json) {
return OrderModel(
id: json['id'] as int,
scoreGoodsId: json['scoreGoodsId'] as int,
goodsName: json['goodsName']?.toString() ?? '',
goodsImage: json['goodsImage'],
goodsContent: json['goodsContent'],
address: json['address']?.toString() ?? '',
createTime: json['createTime']?.toString() ?? '',
score: json['score']?.toString() ?? '',
);
}
}
class MallOrdersController extends GetxController with BaseControllerMixin {
@override
String get builderId => 'mall_orders';
final RxList<OrderModel> orderList = <OrderModel>[].obs;
final RxBool isLoading = true.obs;
int pageNum = 1;
final int pageSize = 50;
@override
void onInit() {
super.onInit();
getOrders();
}
Future<void> getOrders({bool isRefresh = true}) async {
if (isRefresh) {
pageNum = 1;
isLoading.value = true;
updateUi();
}
try {
var response = await HttpService.to.post(
'appointment/score/getScoreExchangeList',
data: {
"status": "",
"pageNum": pageNum.toString(),
"pageSize": pageSize.toString()
},
);
if (response != null && response.data != null) {
var result = BaseModel<Map<String, dynamic>>.fromJson(response.data);
if (result.code == 0 && result.data != null) {
var records = result.data!['records'] as List;
var list = records.map((e) => OrderModel.fromJson(e)).toList();
if (isRefresh) {
orderList.assignAll(list);
} else {
orderList.addAll(list);
}
pageNum++;
}
}
} catch (e) {
log('获取订单列表失败: $e');
} finally {
isLoading.value = false;
updateUi();
}
}
}

View File

@@ -0,0 +1,138 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:getx_scaffold/getx_scaffold.dart';
import 'controller.dart';
class MallOrdersPage extends GetView<MallOrdersController> {
const MallOrdersPage({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<MallOrdersController>(
init: MallOrdersController(),
id: 'mall_orders',
builder: (_) {
return Scaffold(
backgroundColor: const Color(0xFFF7F8FA),
appBar: AppBar(
title: const Text('历史订单'),
backgroundColor: Colors.white,
foregroundColor: Colors.black,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, size: 20),
onPressed: () => Get.back(),
),
),
body: RefreshIndicator(
onRefresh: () => controller.getOrders(isRefresh: true),
child: controller.isLoading.value && controller.orderList.isEmpty
? const Center(child: CircularProgressIndicator())
: _buildOrderList(),
),
);
},
);
}
Widget _buildOrderList() {
if (controller.orderList.isEmpty) {
return const Center(
child: Text('暂无订单记录', style: TextStyle(color: Color(0xFF999999))),
);
}
return ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: controller.orderList.length,
itemBuilder: (context, index) {
final order = controller.orderList[index];
return _buildOrderItem(order);
},
);
}
Widget _buildOrderItem(OrderModel order) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'兑换时间:${order.createTime}',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
color: Color.fromRGBO(107, 114, 128, 1),
),
),
const SizedBox(height: 12),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: order.goodsImage != null
? Image.network(
order.goodsImage!,
width: 80.w,
height: 80.h,
fit: BoxFit.cover,
)
: Container(
width: 80.w,
height: 80.h,
color: Colors.grey[200],
child: const Icon(Icons.image, color: Colors.grey),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
order.goodsName,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
const SizedBox(height: 8),
Row(
children: [
Text(
order.score,
style: TextStyle(
fontSize: 16.sp,
color: Color(0xFF4CAF50),
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 4),
Text(
'积分',
style: TextStyle(fontSize: 11.sp, color: Color(0xFF4CAF50)),
),
],
),
],
),
),
Text(
'x1',
style: TextStyle(color: Color(0xFFCCCCCC), fontSize: 16.sp),
),
],
),
],
),
);
}
}

View File

@@ -0,0 +1,131 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:getx_scaffold/common/index.dart';
import 'package:ln_jq_app/common/login_util.dart';
class MallRulePage extends StatelessWidget {
const MallRulePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromRGBO(64, 199, 154, 1),
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: Colors.white, size: 20),
onPressed: () => Get.back(),
),
),
body: Stack(
children: [
// 顶部装饰图
Positioned(
top: 30,
right: Get.width * 0.15,
child: LoginUtil.getAssImg("rule_bg@2x"),
),
Container(
margin: const EdgeInsets.fromLTRB(20, 100, 20, 20),
padding: const EdgeInsets.all(24),
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/rule_bg_1@2x.png'),
fit: BoxFit.fill,
),
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'积分获取规则',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Color(0xFF2C3E50),
),
),
const SizedBox(height: 30),
_buildRuleItem(
icon: 'tips_1@2x',
title: '每日首次签到积分规则',
content: '每日首签,立得 1 积分',
),
_buildRuleItem(
icon: 'tips_2@2x',
title: '每日预约加氢积分规则',
content: '每日前 2 次预约加氢,各得 1 积分',
),
_buildRuleItem(
icon: 'tips_3@2x',
title: '连续签到累计赠分规则',
content: '连续签到 3 天赠 2 积分7 天赠 5 积分',
),
_buildRuleItem(
icon: 'tips_4@2x',
title: '连续签到周期及断签重置规则',
content: '7 天为一个签到周期,中途断签则重新从第 1 天计算',
),
_buildRuleItem(
icon: 'tips_5@2x',
title: '积分使用规则',
content:
'个人账户内累计的所有有效积分,可在平台积分商城中,用于兑换商城内上架的各类商品、权益或服务,兑换时将按照商品标注的积分值扣除对应积分,积分兑换后不支持撤销、退换,商品兑换规则以积分商城内公示为准。',
),
const SizedBox(height: 40),
const Center(
child: Text(
'本活动最终解释权归官方所有,如有疑问可咨询客服。',
style: TextStyle(color: Color(0xFF999999), fontSize: 12),
),
),
],
),
),
),
],
),
);
}
Widget _buildRuleItem({
required String icon,
required String title,
required String content,
}) {
return Padding(
padding: const EdgeInsets.only(bottom: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
LoginUtil.getAssImg(icon),
const SizedBox(width: 8),
Text(
title,
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
],
),
const SizedBox(height: 6),
Padding(
padding: const EdgeInsets.only(left: 0),
child: Text(
content,
style: TextStyle(fontSize: 13.sp, color: Color(0xFF666666), height: 1.5),
),
),
],
),
);
}
}