2025-07-21 15:46:30 +08:00
|
|
|
|
/// 商品详情页
|
|
|
|
|
library;
|
|
|
|
|
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'dart:convert';
|
|
|
|
|
|
2025-07-21 15:46:30 +08:00
|
|
|
|
import 'package:card_swiper/card_swiper.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:flutter_html/flutter_html.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
import 'package:get/get.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
|
|
|
|
import 'package:loopin/IM/im_message.dart';
|
|
|
|
|
import 'package:loopin/IM/im_service.dart';
|
|
|
|
|
import 'package:loopin/api/shop_api.dart';
|
|
|
|
|
import 'package:loopin/components/my_toast.dart';
|
|
|
|
|
import 'package:loopin/components/network_or_asset_image.dart';
|
2025-08-26 17:38:59 +08:00
|
|
|
|
import 'package:loopin/models/share_type.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/models/summary_type.dart';
|
|
|
|
|
import 'package:loopin/service/http.dart';
|
|
|
|
|
import 'package:loopin/utils/index.dart';
|
|
|
|
|
import 'package:loopin/utils/wxsdk.dart';
|
|
|
|
|
import 'package:shirne_dialog/shirne_dialog.dart';
|
|
|
|
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
2025-07-21 15:46:30 +08:00
|
|
|
|
|
|
|
|
|
import '../../behavior/custom_scroll_behavior.dart';
|
|
|
|
|
import '../../components/backtop.dart';
|
|
|
|
|
|
|
|
|
|
class Goods extends StatefulWidget {
|
|
|
|
|
const Goods({super.key});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
State<Goods> createState() => _GoodsState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _GoodsState extends State<Goods> {
|
2025-08-21 10:50:38 +08:00
|
|
|
|
// late int shopId; //商品id
|
|
|
|
|
dynamic shopObj;
|
2025-07-21 15:46:30 +08:00
|
|
|
|
late ScrollController scrollController = ScrollController();
|
2025-08-21 10:50:38 +08:00
|
|
|
|
final ChatController chatController = Get.find<ChatController>();
|
2025-07-21 15:46:30 +08:00
|
|
|
|
// 滚动位置
|
|
|
|
|
double scrollOffset = 0;
|
2025-08-21 10:50:38 +08:00
|
|
|
|
// 分享列表
|
|
|
|
|
List shareList = [
|
|
|
|
|
{'icon': 'assets/images/share-wx.png', 'label': '好友'},
|
|
|
|
|
{'icon': 'assets/images/share-wx.png', 'label': '微信'},
|
|
|
|
|
{'icon': 'assets/images/share-pyq.png', 'label': '朋友圈'},
|
|
|
|
|
];
|
2025-07-21 15:46:30 +08:00
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
2025-08-30 16:49:21 +08:00
|
|
|
|
final goodsId = Get.arguments['goodsId'];
|
2025-07-21 15:46:30 +08:00
|
|
|
|
scrollController.addListener(() {
|
|
|
|
|
setState(() {
|
|
|
|
|
scrollOffset = scrollController.offset;
|
|
|
|
|
});
|
|
|
|
|
});
|
2025-08-30 16:49:21 +08:00
|
|
|
|
shopDetail(goodsId);
|
2025-07-21 15:46:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
scrollController.dispose();
|
|
|
|
|
super.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-21 10:50:38 +08:00
|
|
|
|
///商品详情
|
2025-08-30 16:49:21 +08:00
|
|
|
|
void shopDetail(goodsId) async {
|
2025-08-21 10:50:38 +08:00
|
|
|
|
try {
|
2025-08-30 16:49:21 +08:00
|
|
|
|
final res = await Http.get('${ShopApi.shopDetail}/$goodsId');
|
2025-08-21 10:50:38 +08:00
|
|
|
|
logger.e(res['data']);
|
|
|
|
|
setState(() {
|
|
|
|
|
shopObj = res['data']; // 注意取 data 部分
|
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
logger.e(e);
|
|
|
|
|
Get.back();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-01 18:18:26 +08:00
|
|
|
|
///创建定点杆
|
|
|
|
|
createOrder(String goodsId) async {
|
|
|
|
|
var params ={
|
|
|
|
|
"type": 1, // 订单类型:1->团购;2->拼团;3->秒杀;
|
|
|
|
|
"distribution": 1, // 配送方式 1->到店核销;2->自提;3->配送;
|
|
|
|
|
"skuItemBOList": [
|
|
|
|
|
{
|
|
|
|
|
"skuId": goodsId,
|
|
|
|
|
"quantity": 1
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
print('下单请求参数---->${params}');
|
|
|
|
|
try {
|
|
|
|
|
final res = await Http.post('${ShopApi.createGoodsOrder}', data: params);
|
|
|
|
|
var resData = res['data'];
|
|
|
|
|
print('1111111111111111111111111---->${res}');
|
|
|
|
|
if(resData['id'].isNotEmpty){
|
|
|
|
|
return resData['id'];
|
|
|
|
|
}else{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
logger.e(e);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-21 10:50:38 +08:00
|
|
|
|
void handleShareClick(int index) {
|
|
|
|
|
final description = shopObj['describe']; // 商品描述
|
|
|
|
|
if (index == 1) {
|
|
|
|
|
// 好友
|
2025-08-26 17:38:59 +08:00
|
|
|
|
Wxsdk.shareToFriend(title: '快看看我分享的商品', description: description, webpageUrl: '${ShareType.shop.name}?id=${shopObj['id']}');
|
2025-08-21 10:50:38 +08:00
|
|
|
|
} else if (index == 2) {
|
|
|
|
|
// 朋友圈
|
2025-08-26 17:38:59 +08:00
|
|
|
|
Wxsdk.shareToTimeline(title: '快看看我分享的商品', webpageUrl: '${ShareType.shop.name}?id=${shopObj['id']}');
|
2025-08-21 10:50:38 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void handlCoverClick(V2TimConversation conv) async {
|
|
|
|
|
// 发送VideoMsg,获取当前视频信息
|
|
|
|
|
final userId = conv.userID;
|
|
|
|
|
//price,title,url,sell
|
|
|
|
|
final makeJson = jsonEncode({
|
|
|
|
|
"price": shopObj['price'],
|
|
|
|
|
"title": shopObj['describe'],
|
|
|
|
|
"url": shopObj['pic'],
|
2025-09-03 11:25:31 +08:00
|
|
|
|
"sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
});
|
|
|
|
|
final res = await IMMessage().createCustomMessage(
|
|
|
|
|
data: makeJson,
|
|
|
|
|
);
|
|
|
|
|
if (res.success) {
|
|
|
|
|
final sendRes = await IMMessage().sendMessage(msg: res.data!.messageInfo!, toUserID: userId, cloudCustomData: SummaryType.shareTuangou);
|
|
|
|
|
if (sendRes.success) {
|
|
|
|
|
MyToast().tip(
|
|
|
|
|
title: '分享成功',
|
|
|
|
|
position: 'center',
|
|
|
|
|
type: 'success',
|
|
|
|
|
);
|
|
|
|
|
Get.back();
|
|
|
|
|
} else {
|
|
|
|
|
logger.e(res.desc);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
logger.e(res.desc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 分享弹框
|
|
|
|
|
void handleShare() {
|
|
|
|
|
if (chatController.chatList.isNotEmpty) {
|
|
|
|
|
chatController.getConversationList();
|
|
|
|
|
}
|
|
|
|
|
showModalBottomSheet(
|
|
|
|
|
backgroundColor: Colors.white,
|
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
|
|
|
|
|
),
|
|
|
|
|
clipBehavior: Clip.antiAlias,
|
|
|
|
|
context: context,
|
|
|
|
|
isScrollControlled: true,
|
|
|
|
|
builder: (context) {
|
|
|
|
|
return Material(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
child: Padding(
|
|
|
|
|
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
|
|
|
|
child: Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
children: [
|
|
|
|
|
// 分享列表
|
|
|
|
|
SizedBox(
|
|
|
|
|
height: 110,
|
|
|
|
|
child: ListView.builder(
|
|
|
|
|
scrollDirection: Axis.horizontal,
|
|
|
|
|
itemCount: shareList.length,
|
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
|
return GestureDetector(
|
|
|
|
|
onTap: () => handleShareClick(index),
|
|
|
|
|
child: Container(
|
|
|
|
|
width: 64,
|
|
|
|
|
margin: EdgeInsets.symmetric(horizontal: 8.0),
|
|
|
|
|
child: Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
children: [
|
|
|
|
|
Image.asset('${shareList[index]['icon']}', width: 48.0),
|
|
|
|
|
SizedBox(height: 5),
|
|
|
|
|
Text(
|
|
|
|
|
'${shareList[index]['label']}',
|
|
|
|
|
style: TextStyle(fontSize: 12.0),
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
// 会话列表
|
|
|
|
|
Obx(() {
|
|
|
|
|
// 这里过滤掉有分组的会话
|
|
|
|
|
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
|
|
|
|
if (filteredList.isEmpty) return SizedBox.shrink();
|
|
|
|
|
return SizedBox(
|
|
|
|
|
height: 110,
|
|
|
|
|
child: ListView.builder(
|
|
|
|
|
scrollDirection: Axis.horizontal,
|
|
|
|
|
itemCount: filteredList.length,
|
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
|
return GestureDetector(
|
|
|
|
|
// 点击分享
|
|
|
|
|
onTap: () => handlCoverClick(filteredList[index].conversation),
|
|
|
|
|
child: Container(
|
|
|
|
|
width: 64,
|
|
|
|
|
margin: EdgeInsets.symmetric(horizontal: 8.0),
|
|
|
|
|
child: Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
children: [
|
|
|
|
|
// Image.asset('${chatController.chatList[index].faceUrl}', width: 48.0),
|
|
|
|
|
NetworkOrAssetImage(
|
|
|
|
|
imageUrl: filteredList[index].faceUrl,
|
|
|
|
|
width: 48.0,
|
|
|
|
|
height: 48.0,
|
|
|
|
|
),
|
|
|
|
|
SizedBox(height: 5),
|
|
|
|
|
Text(
|
|
|
|
|
'${filteredList[index].conversation.showName}',
|
|
|
|
|
style: TextStyle(fontSize: 12.0),
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
// 取消按钮
|
|
|
|
|
SafeArea(
|
|
|
|
|
top: false,
|
|
|
|
|
child: InkWell(
|
|
|
|
|
onTap: () => Get.back(),
|
|
|
|
|
child: Container(
|
|
|
|
|
alignment: Alignment.center,
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
height: 50.0,
|
|
|
|
|
color: Colors.grey[50],
|
|
|
|
|
child: Text(
|
|
|
|
|
'取消',
|
|
|
|
|
style: TextStyle(color: Colors.black87),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-21 15:46:30 +08:00
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
2025-08-21 10:50:38 +08:00
|
|
|
|
if (shopObj == null) {
|
|
|
|
|
return Center(child: CircularProgressIndicator());
|
|
|
|
|
}
|
|
|
|
|
String swiperInfo = shopObj['albumPics'] ?? "";
|
|
|
|
|
List<String> swiperList;
|
|
|
|
|
if (swiperInfo.isNotEmpty) {
|
|
|
|
|
swiperList = swiperInfo.split(','); // 商品详情轮播图
|
|
|
|
|
} else {
|
|
|
|
|
swiperList = [];
|
|
|
|
|
}
|
|
|
|
|
dynamic attr = shopObj['productAttr']; //json数据
|
|
|
|
|
List<dynamic> attrList = [];
|
|
|
|
|
if (!Utils.isEmpty(attr)) {
|
|
|
|
|
attrList = jsonDecode(attr);
|
|
|
|
|
}
|
|
|
|
|
logger.e(attrList);
|
|
|
|
|
|
2025-07-21 15:46:30 +08:00
|
|
|
|
return Scaffold(
|
|
|
|
|
backgroundColor: Colors.grey[50],
|
|
|
|
|
body: CustomScrollView(
|
|
|
|
|
scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
|
|
|
|
controller: scrollController,
|
|
|
|
|
slivers: [
|
|
|
|
|
SliverAppBar(
|
|
|
|
|
backgroundColor: Colors.transparent,
|
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
|
pinned: true,
|
|
|
|
|
expandedHeight: 280.0,
|
|
|
|
|
titleSpacing: 10.0,
|
|
|
|
|
leading: IconButton(
|
|
|
|
|
icon: Icon(
|
|
|
|
|
Icons.arrow_back,
|
|
|
|
|
size: 20.0,
|
|
|
|
|
),
|
|
|
|
|
style: ButtonStyle(
|
|
|
|
|
backgroundColor: WidgetStateProperty.all(Colors.black.withAlpha(20)),
|
|
|
|
|
),
|
|
|
|
|
onPressed: () {
|
|
|
|
|
Get.back();
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
actions: [
|
|
|
|
|
IconButton(
|
|
|
|
|
icon: Icon(
|
|
|
|
|
Icons.share,
|
|
|
|
|
size: 20.0,
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
onPressed: () {
|
|
|
|
|
// 分享
|
|
|
|
|
handleShare();
|
|
|
|
|
},
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
// 自定义伸缩区域(轮播图)
|
|
|
|
|
flexibleSpace: Container(
|
2025-08-21 10:50:38 +08:00
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
gradient: LinearGradient(
|
|
|
|
|
begin: Alignment.topLeft,
|
|
|
|
|
end: Alignment.bottomRight,
|
|
|
|
|
colors: [Color(0xFFFF5000), Color(0xFFFFAA00)],
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
child: FlexibleSpaceBar(
|
|
|
|
|
background: ScrollConfiguration(
|
|
|
|
|
behavior: CustomScrollBehavior(),
|
|
|
|
|
child: Swiper.children(
|
|
|
|
|
pagination: SwiperPagination(
|
|
|
|
|
builder: DotSwiperPaginationBuilder(
|
|
|
|
|
color: Colors.white70,
|
|
|
|
|
activeColor: Colors.white,
|
|
|
|
|
)),
|
|
|
|
|
indicatorLayout: PageIndicatorLayout.SCALE,
|
2025-08-21 10:50:38 +08:00
|
|
|
|
children: swiperList.map((sw) => NetworkOrAssetImage(imageUrl: sw)).toList(),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
SliverToBoxAdapter(
|
|
|
|
|
child: ScrollConfiguration(
|
|
|
|
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
Container(
|
|
|
|
|
padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 25.0),
|
|
|
|
|
decoration: BoxDecoration(
|
2025-08-21 10:50:38 +08:00
|
|
|
|
gradient: LinearGradient(
|
|
|
|
|
begin: Alignment.topLeft,
|
|
|
|
|
end: Alignment.bottomRight,
|
|
|
|
|
colors: [Color(0xFFFF5000), Color(0xFFFFAA00)],
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
spacing: 5.0,
|
|
|
|
|
children: [
|
|
|
|
|
Row(
|
|
|
|
|
spacing: 5.0,
|
|
|
|
|
children: [
|
2025-08-21 10:50:38 +08:00
|
|
|
|
// 原价
|
|
|
|
|
// Text(
|
|
|
|
|
// '¥${shopObj['price']}',
|
|
|
|
|
// style: TextStyle(
|
|
|
|
|
// color: Colors.white,
|
|
|
|
|
// fontSize: 16.0,
|
|
|
|
|
// decoration: TextDecoration.lineThrough,
|
|
|
|
|
// decorationColor: Colors.black,
|
|
|
|
|
// decorationThickness: 1.5,
|
|
|
|
|
// ),
|
|
|
|
|
// ),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
Container(
|
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
borderRadius: BorderRadius.circular(50.0),
|
|
|
|
|
),
|
|
|
|
|
child: Text(
|
2025-08-21 10:50:38 +08:00
|
|
|
|
'¥${shopObj['price']}',
|
2025-07-21 15:46:30 +08:00
|
|
|
|
style: TextStyle(color: Colors.red, fontSize: 12.0),
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
Text(
|
|
|
|
|
// '已售${Utils().graceNumber(shopObj['sales'] ?? 0)}',
|
2025-09-03 11:25:31 +08:00
|
|
|
|
'已售${Utils.graceNumber(int.tryParse(shopObj['sales']?.toString() ?? '0') ?? 0)}',
|
2025-08-21 10:50:38 +08:00
|
|
|
|
style: TextStyle(color: Colors.white, fontSize: 12.0),
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Container(
|
|
|
|
|
padding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
width: double.infinity,
|
2025-07-21 15:46:30 +08:00
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Color(0xFFFAFAFA),
|
|
|
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
|
|
|
|
|
),
|
|
|
|
|
transform: Matrix4.translationValues(0.0, -15.0, 0.0),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
// 标题
|
|
|
|
|
Container(
|
|
|
|
|
padding: EdgeInsets.all(5.0),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
child: Align(
|
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
|
child: Text.rich(
|
2025-07-21 15:46:30 +08:00
|
|
|
|
TextSpan(
|
2025-08-21 10:50:38 +08:00
|
|
|
|
children: [
|
|
|
|
|
WidgetSpan(
|
|
|
|
|
alignment: PlaceholderAlignment.middle,
|
|
|
|
|
child: Container(
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: const Color(0xFFFF5000),
|
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
|
|
|
|
),
|
|
|
|
|
child: Text(
|
|
|
|
|
shopObj['productCategoryName'] ?? '未知分类名称',
|
|
|
|
|
style: const TextStyle(
|
|
|
|
|
fontSize: 12.0,
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const WidgetSpan(child: SizedBox(width: 4)),
|
|
|
|
|
TextSpan(
|
|
|
|
|
text: '${shopObj['describe'] ?? ''}',
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
fontSize: 14.0,
|
|
|
|
|
fontWeight: FontWeight.w700,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
maxLines: 2,
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
textAlign: TextAlign.left,
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
|
2025-07-21 15:46:30 +08:00
|
|
|
|
// 规格
|
|
|
|
|
Container(
|
|
|
|
|
margin: EdgeInsets.only(top: 10.0),
|
|
|
|
|
padding: EdgeInsets.all(10.0),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
borderRadius: BorderRadius.circular(15.0),
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
// 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),
|
|
|
|
|
// ),
|
|
|
|
|
// ),
|
|
|
|
|
// ],
|
|
|
|
|
// ),
|
|
|
|
|
// ],
|
|
|
|
|
// ),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
child: Column(
|
2025-08-21 10:50:38 +08:00
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: attrList.map<Widget>((attr) {
|
|
|
|
|
final attrName = attr['name'] ?? '';
|
|
|
|
|
final options = attr['options'] as List<dynamic>? ?? [];
|
|
|
|
|
final optionNames = options.map((o) => o['name']).join(' / ');
|
|
|
|
|
return Row(
|
2025-07-21 15:46:30 +08:00
|
|
|
|
children: [
|
2025-08-21 10:50:38 +08:00
|
|
|
|
Text(
|
|
|
|
|
'$attrName: ',
|
|
|
|
|
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: Text(
|
2025-08-21 10:50:38 +08:00
|
|
|
|
optionNames,
|
|
|
|
|
style: TextStyle(fontSize: 12),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
2025-08-21 10:50:38 +08:00
|
|
|
|
);
|
|
|
|
|
}).toList(),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
// 详情
|
|
|
|
|
Container(
|
2025-08-21 10:50:38 +08:00
|
|
|
|
margin: EdgeInsets.only(top: 10.0),
|
|
|
|
|
padding: EdgeInsets.all(10.0),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
borderRadius: BorderRadius.circular(15.0),
|
|
|
|
|
),
|
|
|
|
|
child: Html(
|
|
|
|
|
data: shopObj['detailMobileHtml'] ?? '暂无',
|
|
|
|
|
)),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
// 商品导航栏
|
|
|
|
|
bottomNavigationBar: SafeArea(
|
|
|
|
|
bottom: true,
|
|
|
|
|
child: Container(
|
|
|
|
|
height: 50.0,
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
|
|
|
|
child: Row(
|
|
|
|
|
children: [
|
|
|
|
|
Expanded(
|
|
|
|
|
child: Row(
|
|
|
|
|
spacing: 15.0,
|
|
|
|
|
children: [
|
2025-08-21 10:50:38 +08:00
|
|
|
|
// Column(
|
|
|
|
|
// mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
|
// children: [
|
|
|
|
|
// Icon(
|
|
|
|
|
// Icons.store,
|
|
|
|
|
// color: Color(0xFFFF5000),
|
|
|
|
|
// size: 18.0,
|
|
|
|
|
// ),
|
|
|
|
|
// Text(
|
|
|
|
|
// '店铺',
|
|
|
|
|
// style: TextStyle(fontSize: 12.0),
|
|
|
|
|
// )
|
|
|
|
|
// ],
|
|
|
|
|
// ),
|
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: () async {
|
2025-08-26 17:38:59 +08:00
|
|
|
|
// 可以在这里打开聊天
|
2025-08-21 10:50:38 +08:00
|
|
|
|
logger.i('联系客服');
|
2025-08-26 17:38:59 +08:00
|
|
|
|
final res = await ImService.instance.getConversation(conversationID: 'c2c_${shopObj['tenantId']}');
|
2025-08-21 10:50:38 +08:00
|
|
|
|
V2TimConversation conversation = res.data;
|
|
|
|
|
logger.i(conversation.toLogString());
|
|
|
|
|
if (res.success) {
|
|
|
|
|
// 客服聊天不用检测关注关系
|
|
|
|
|
conversation.showName = conversation.showName ?? shopObj['storeName'];
|
|
|
|
|
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,
|
2025-07-21 15:46:30 +08:00
|
|
|
|
size: 18.0,
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
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),
|
|
|
|
|
// )
|
|
|
|
|
// ],
|
|
|
|
|
// ),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
Container(
|
|
|
|
|
alignment: Alignment.center,
|
|
|
|
|
height: 36.0,
|
|
|
|
|
clipBehavior: Clip.antiAlias,
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Color(0xFFFFEBEB),
|
|
|
|
|
borderRadius: BorderRadius.circular(30.0),
|
|
|
|
|
),
|
|
|
|
|
child: Row(
|
|
|
|
|
children: [
|
2025-08-21 10:50:38 +08:00
|
|
|
|
// Padding(
|
|
|
|
|
// padding: EdgeInsets.symmetric(horizontal: 10.0),
|
|
|
|
|
// child: Text(
|
|
|
|
|
// '加入购物车',
|
|
|
|
|
// style: TextStyle(color: Color(0xFFFF5000), fontSize: 14.0),
|
|
|
|
|
// ),
|
|
|
|
|
// ),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
Container(
|
|
|
|
|
alignment: Alignment.center,
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
|
|
|
|
color: Color(0xFFFF5000),
|
2025-08-26 17:38:59 +08:00
|
|
|
|
child: GestureDetector(
|
2025-09-01 18:18:26 +08:00
|
|
|
|
onTap: () async {
|
2025-08-26 17:38:59 +08:00
|
|
|
|
// 这里走生成预支付订单,拿到orderId
|
2025-09-01 18:18:26 +08:00
|
|
|
|
// String orderId = '1958380183857659904'; //测试数据
|
|
|
|
|
String orderId = await createOrder(shopObj['skuList'][0]['id']);
|
|
|
|
|
if(orderId.isNotEmpty){
|
|
|
|
|
Get.toNamed('/order/detail', arguments: orderId);
|
|
|
|
|
}else{
|
|
|
|
|
MyDialog.toast('生成订单失败', icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
|
|
|
|
}
|
2025-08-26 17:38:59 +08:00
|
|
|
|
},
|
|
|
|
|
child: Text(
|
|
|
|
|
'立即购买',
|
|
|
|
|
style: TextStyle(color: Colors.white, fontSize: 14.0),
|
|
|
|
|
),
|
2025-07-21 15:46:30 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
// 返回顶部
|
|
|
|
|
floatingActionButton: Backtop(controller: scrollController, offset: scrollOffset),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|