From a777aca12164de11cfe7e73d50a01fd29398a648 Mon Sep 17 00:00:00 2001 From: Seven Tsui <16225583+youyouliangshao@user.noreply.gitee.com> Date: Fri, 19 Sep 2025 17:56:34 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=86=E8=8A=82=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/goods/detail.dart | 395 +++++++++++++++++++++--------------- lib/pages/order/detail.dart | 8 +- 2 files changed, 237 insertions(+), 166 deletions(-) diff --git a/lib/pages/goods/detail.dart b/lib/pages/goods/detail.dart index 0c9b7d0..0476695 100644 --- a/lib/pages/goods/detail.dart +++ b/lib/pages/goods/detail.dart @@ -46,6 +46,11 @@ class _GoodsState extends State { {'icon': 'assets/images/share-pyq.png', 'label': '朋友圈'}, ]; + // 新增状态变量 + Map selectedAttributes = {}; // 存储选中的属性 + dynamic selectedSku; // 当前选中的SKU + int _quantity = 1; // 商品数量 + @override void initState() { super.initState(); @@ -71,20 +76,82 @@ class _GoodsState extends State { logger.e(res['data']); setState(() { shopObj = res['data']; // 注意取 data 部分 + // 初始化选中的SKU为第一个 + if (shopObj != null && shopObj['skuList'] != null && shopObj['skuList'].isNotEmpty) { + // selectedSku = shopObj['skuList'][0]; + // 默认选中每一个分类中的第一条数据 + dynamic attr = shopObj['productAttr']; + List attrList = []; + if (!Utils.isEmpty(attr)) { + attrList = jsonDecode(attr); + } + + // 清空已选属性 + selectedAttributes.clear(); + + // 为每个属性选择第一个选项 + for (var attr in attrList) { + final attrName = attr['name'] ?? ''; + final options = attr['options'] as List? ?? []; + if (options.isNotEmpty) { + final firstOption = options[0]['name'] ?? ''; + selectedAttributes[attrName] = firstOption; + } + } + + // 根据选中的属性定位到对应的商品 + locateSelectedSku(); + } }); } catch (e) { logger.e(e); Get.back(); } } +// 根据选中的属性定位到对应的SKU +void locateSelectedSku() { + if (shopObj != null && shopObj['skuList'] != null) { + for (var sku in shopObj['skuList']) { + try { + final spData = jsonDecode(sku['spData'] ?? '{}'); + bool match = true; + // 检查所有已选属性是否匹配 + selectedAttributes.forEach((key, value) { + if (spData[key] != value) { + match = false; + } + }); + + if (match) { + setState(() { + selectedSku = sku; + }); + print('333333333333333333'); + print(sku); + break; + } + } catch (e) { + logger.e('解析spData错误: $e'); + } + } + } +} + +// 处理属性选择 +void handleAttributeSelect(String attrName, String optionName) { + setState(() { + selectedAttributes[attrName] = optionName; + locateSelectedSku(); // 选择属性后重新定位SKU + }); +} ///创建定点杆 createOrder(String goodsId) async { var params = { "type": 1, // 订单类型:1->团购;2->拼团;3->秒杀; "distribution": 1, // 配送方式 1->到店核销;2->自提;3->配送; "skuItemBOList": [ - {"skuId": goodsId, "quantity": 1} + {"skuId": goodsId, "quantity": _quantity} ] }; print('下单请求参数---->$params'); @@ -271,6 +338,10 @@ class _GoodsState extends State { }, ); } + // 检查属性是否被选中 + bool isAttributeSelected(String attrName, String optionName) { + return selectedAttributes[attrName] == optionName; + } @override Widget build(BuildContext context) { @@ -378,17 +449,7 @@ class _GoodsState extends State { Row( spacing: 5.0, children: [ - // 原价 - // Text( - // '¥${shopObj['price']}', - // style: TextStyle( - // color: Colors.white, - // fontSize: 16.0, - // decoration: TextDecoration.lineThrough, - // decorationColor: Colors.black, - // decorationThickness: 1.5, - // ), - // ), + // 显示当前选中的SKU价格或默认价格 Container( padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0), decoration: BoxDecoration( @@ -396,12 +457,11 @@ class _GoodsState extends State { borderRadius: BorderRadius.circular(50.0), ), child: Text( - '¥${shopObj['price']}', + '¥${selectedSku != null ? selectedSku['price'] : shopObj['price']}', style: TextStyle(color: Colors.red, fontSize: 12.0), ), ), Text( - // '已售${Utils().graceNumber(shopObj['sales'] ?? 0)}', '已售${Utils.graceNumber(int.tryParse(shopObj['sales']?.toString() ?? '0') ?? 0)}', style: TextStyle(color: Colors.white, fontSize: 12.0), ), @@ -462,85 +522,173 @@ class _GoodsState extends State { ), ), + // 规格 // 规格 Container( + width: double.infinity, margin: EdgeInsets.only(top: 10.0), padding: EdgeInsets.all(10.0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15.0), ), - // child: Column( - // spacing: 10.0, - // children: [ - // Row( - // spacing: 5.0, - // children: [ - // Icon( - // Icons.timer, - // size: 16.0, - // ), - // Expanded( - // child: Text( - // '本商品请于2025.01.25前进行核销', - // style: TextStyle(fontSize: 12.0), - // ), - // ), - // ], - // ), - // Row( - // spacing: 5.0, - // children: [ - // Icon( - // Icons.house_outlined, - // size: 16.0, - // ), - // Expanded( - // child: Text( - // '营业时间:7x24', - // style: TextStyle(fontSize: 12.0), - // ), - // ), - // ], - // ), - // Row( - // spacing: 5.0, - // children: [ - // Icon( - // Icons.location_on, - // size: 16.0, - // ), - // Expanded( - // child: Text( - // '河北省唐山市玉田县', - // style: TextStyle(fontSize: 12.0), - // ), - // ), - // ], - // ), - // ], - // ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: attrList.map((attr) { - final attrName = attr['name'] ?? ''; - final options = attr['options'] as List? ?? []; - final optionNames = options.map((o) => o['name']).join(' / '); - return Row( + children: [ + // 商品缩略图和数量选择 + Row( children: [ - Text( - '$attrName: ', - style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold), - ), - Expanded( - child: Text( - optionNames, - style: TextStyle(fontSize: 12), + // 商品缩略图 + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + image: DecorationImage( + image: NetworkImage(selectedSku['pic'] != null ? selectedSku['pic'] : shopObj['pic']), + fit: BoxFit.cover, + ), ), ), + SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '¥${selectedSku != null ? selectedSku['price'] : shopObj['price']}', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.red, + ), + ), + SizedBox(height: 4), + Text( + '库存: ${selectedSku != null ? selectedSku['stock'] : shopObj['stock']}', + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + ), + ), + ], + ), + ), + // 数量加减按钮 + Row( + children: [ + // 减少按钮 + GestureDetector( + onTap: () { + setState(() { + if (_quantity > 1) { + _quantity--; + } + }); + }, + child: Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: _quantity > 1 ? Colors.grey[200] : Colors.grey[100], + borderRadius: BorderRadius.circular(4), + border: Border.all(color: Colors.grey[300]!), + ), + child: Icon( + Icons.remove, + size: 16, + color: _quantity > 1 ? Colors.black : Colors.grey[400], + ), + ), + ), + SizedBox(width: 8), + // 数量显示 + Container( + width: 40, + alignment: Alignment.center, + child: Text( + '$_quantity', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ), + SizedBox(width: 8), + // 增加按钮 + GestureDetector( + onTap: () { + setState(() { + final maxStock = selectedSku != null ? selectedSku['stock'] : shopObj['stock']; + if (_quantity < maxStock) { + _quantity++; + } + }); + }, + child: Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(4), + border: Border.all(color: Colors.grey[300]!), + ), + child: Icon( + Icons.add, + size: 16, + ), + ), + ), + ], + ), ], - ); - }).toList(), + ), + Divider(height: 20, color: Colors.grey[200]), + // 属性选择 + ...attrList.map((attr) { + final attrName = attr['name'] ?? ''; + final options = attr['options'] as List? ?? []; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '$attrName:', + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: options.map((option) { + final optionName = option['name'] ?? ''; + final isSelected = isAttributeSelected(attrName, optionName); + + return GestureDetector( + onTap: () => handleAttributeSelect(attrName, optionName), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: isSelected ? Color(0xFFFFF0F0) : Colors.grey[200], + borderRadius: BorderRadius.circular(20), + border: isSelected ? Border.all(color: Color(0xFFFF5000), width: 1) : null, + ), + child: Text( + optionName, + style: TextStyle( + fontSize: 12, + color: isSelected ? Color(0xFFFF5000) : Colors.black87, + ), + ), + ), + ); + }).toList(), + ), + SizedBox(height: 12), + ], + ); + }).toList(), + ], ), ), // 详情 @@ -576,77 +724,7 @@ class _GoodsState extends State { child: Row( spacing: 15.0, children: [ - // Column( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // Icon( - // Icons.store, - // color: Color(0xFFFF5000), - // size: 18.0, - // ), - // Text( - // '店铺', - // style: TextStyle(fontSize: 12.0), - // ) - // ], - // ), - - // 联系商家 - // GestureDetector( - // onTap: () async { - // // 可以在这里打开聊天 - // logger.i('联系客服:$shopObj'); - // final res = await ImService.instance.getConversation(conversationID: 'c2c_${shopObj['tenantId']}'); - // V2TimConversation conversation = res.data; - // logger.i(conversation.toLogString()); - // if (res.success) { - // // 客服聊天不用检测关注关系 - // V2TimUserFullInfo? sellerInfo; - // final resIm = await ImService.instance.otherInfo(shopObj['tenantId']); - // if (resIm.success && resIm.data != null) { - // sellerInfo = resIm.data!; - // logger.i(sellerInfo!.toLogString()); - // } else { - // logger.e(resIm.desc); - // } - // conversation.showName = conversation.showName ?? sellerInfo!.nickName; - // Get.toNamed('/chat', arguments: conversation); - // } else { - // MyDialog.toast(res.desc, icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200))); - // } - // }, - // child: Column( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // Icon( - // Icons.child_care_outlined, - // size: 18.0, - // ), - // Text( - // '联系商家', - // style: TextStyle(fontSize: 12.0), - // ) - // ], - // ), - // ) - - // Column( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // Badge.count( - // backgroundColor: Color(0xFFFF5000), - // count: 6, - // child: Icon( - // Icons.shopping_cart_outlined, - // size: 18.0, - // ), - // ), - // Text( - // '购物车', - // style: TextStyle(fontSize: 12.0), - // ) - // ], - // ), + // 这里可以保留原有的图标按钮 ], ), ), @@ -658,15 +736,8 @@ class _GoodsState extends State { color: Color(0xFFFFEBEB), borderRadius: BorderRadius.circular(30.0), ), - child: Row( + child: shopObj['canOrder'] == true?Row( children: [ - // Padding( - // padding: EdgeInsets.symmetric(horizontal: 10.0), - // child: Text( - // '加入购物车', - // style: TextStyle(color: Color(0xFFFF5000), fontSize: 14.0), - // ), - // ), Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 20.0), @@ -674,8 +745,8 @@ class _GoodsState extends State { child: GestureDetector( onTap: () async { // 这里走生成预支付订单,拿到orderId - // String orderId = '1958380183857659904'; //测试数据 - String orderId = await createOrder(shopObj['skuList'][0]['id']); + String skuId = selectedSku != null ? selectedSku['id'] : shopObj['skuList'][0]['id']; + String orderId = await createOrder(skuId); if (orderId.isNotEmpty) { Get.toNamed('/order/detail', arguments: {'orderId': orderId}); } else { @@ -689,7 +760,7 @@ class _GoodsState extends State { ), ), ], - ), + ):null, ), ], ), @@ -699,4 +770,4 @@ class _GoodsState extends State { floatingActionButton: Backtop(controller: scrollController, offset: scrollOffset), ); } -} +} \ No newline at end of file diff --git a/lib/pages/order/detail.dart b/lib/pages/order/detail.dart index 9736ad3..fcccbdf 100644 --- a/lib/pages/order/detail.dart +++ b/lib/pages/order/detail.dart @@ -784,7 +784,7 @@ class _OrderDetailState extends State with SingleTickerProviderStat ), Spacer(), Text( - 'x${productInfo['buyNum']?.toString() ?? '1'}', + 'x${productInfo['quantity']?.toString() ?? '1'}', style: TextStyle(color: Colors.grey), ), ], @@ -829,7 +829,7 @@ class _OrderDetailState extends State with SingleTickerProviderStat size: 18.0, ), onTap: () async { - await Clipboard.setData(ClipboardData(text: _orderId)); + await Clipboard.setData(ClipboardData(text: orderGoodsInfo?['orderSn'])); MyDialog.toast('订单已复制到剪切板', icon: Icon(Icons.check_circle)); }, ) @@ -838,9 +838,9 @@ class _OrderDetailState extends State with SingleTickerProviderStat SizedBox(height: 10), Column( children: [ - _buildOrderInfoRow('订单号', orderGoodsInfo?['orderId'] ?? ''), + _buildOrderInfoRow('订单号', orderGoodsInfo?['orderSn'] ?? ''), _buildOrderInfoRow('下单时间', orderGoodsInfo?['createTime'] ?? ''), - _buildOrderInfoRow('购买数量', (productInfo['buyNum'] ?? 0).toString()), + _buildOrderInfoRow('购买数量', (productInfo['quantity'] ?? 0).toString()), _buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['totalAmount'] ?? '0.00'}'), _buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount'] ?? '0.00'}'), ],