|
|
|
@ -284,6 +284,7 @@ class MessageBubble extends GetView<AgentController> { |
|
|
|
// 旅游 |
|
|
|
return _buildTravelContent(context, travellist); |
|
|
|
} else if (meta!.keys.contains('card_navigation')) { |
|
|
|
final destination = meta!['card_navigation']['destination'] as String; |
|
|
|
final rawList = meta!['card_navigation']['items'] as List; |
|
|
|
final list = rawList.map((e) => Map<String, dynamic>.from(e)).toList(); |
|
|
|
final rawNavigation = meta!['card_navigation']['navigation']; |
|
|
|
@ -291,7 +292,8 @@ class MessageBubble extends GetView<AgentController> { |
|
|
|
rawNavigation is Map<String, dynamic> ? rawNavigation : null; |
|
|
|
print("导航实时数据 ${rawNavigation}"); |
|
|
|
// 导航 |
|
|
|
return navigationCard(context, modes: list, navigation: navigation); |
|
|
|
return navigationCard(context, |
|
|
|
destination: destination, modes: list, navigation: navigation); |
|
|
|
} |
|
|
|
|
|
|
|
// if (message.contains('播放列表') || message.contains('音乐')) { |
|
|
|
@ -1173,8 +1175,37 @@ class MessageBubble extends GetView<AgentController> { |
|
|
|
return Icons.help_outline; |
|
|
|
} |
|
|
|
|
|
|
|
String formatDistance(String distanceStr) { |
|
|
|
final distance = double.tryParse(distanceStr); |
|
|
|
if (distance == null) return distanceStr; // 如果无法解析,则返回原始字符串 |
|
|
|
|
|
|
|
if (distance >= 1000) { |
|
|
|
final km = distance / 1000; |
|
|
|
return '${km.toStringAsFixed(1)} 公里'; |
|
|
|
} else { |
|
|
|
return '${distance.toStringAsFixed(0)} 米'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
String formatDuration(String secondsStr) { |
|
|
|
int totalSeconds = int.tryParse(secondsStr) ?? 0; |
|
|
|
Duration duration = Duration(seconds: totalSeconds); |
|
|
|
|
|
|
|
int hours = duration.inHours; |
|
|
|
int minutes = duration.inMinutes.remainder(60); |
|
|
|
int seconds = duration.inSeconds.remainder(60); |
|
|
|
|
|
|
|
List<String> parts = []; |
|
|
|
if (hours > 0) parts.add('$hours小时'); |
|
|
|
if (minutes > 0) parts.add('$minutes分钟'); |
|
|
|
if (seconds > 0 || parts.isEmpty) parts.add('$seconds秒'); |
|
|
|
|
|
|
|
return parts.join(''); |
|
|
|
} |
|
|
|
|
|
|
|
Widget navigationCard( |
|
|
|
BuildContext context, { |
|
|
|
required String destination, |
|
|
|
required List<Map<String, dynamic>> modes, |
|
|
|
Map<String, dynamic>? navigation, |
|
|
|
double height = 300, |
|
|
|
@ -1224,16 +1255,22 @@ class MessageBubble extends GetView<AgentController> { |
|
|
|
children: List.generate(modes.length, (index) { |
|
|
|
final isSelected = index == currentIndex; |
|
|
|
return GestureDetector( |
|
|
|
onTap: () { |
|
|
|
onTap: modes[index]['enable'] as bool |
|
|
|
? () { |
|
|
|
setInnerState(() => currentIndex = index); |
|
|
|
}, |
|
|
|
} |
|
|
|
: null, |
|
|
|
child: SizedBox( |
|
|
|
width: 50, |
|
|
|
child: Text( |
|
|
|
modes[index]["ntype"] as String, |
|
|
|
(modes[index]["ntype"] as String).tr, |
|
|
|
textAlign: TextAlign.center, |
|
|
|
style: TextStyle( |
|
|
|
color: isSelected ? Colors.blue : Colors.black87, |
|
|
|
color: modes[index]['enable'] |
|
|
|
? isSelected |
|
|
|
? Colors.blue |
|
|
|
: Colors.black87 |
|
|
|
: Colors.grey, |
|
|
|
fontSize: 12, |
|
|
|
fontWeight: FontWeight.w500, |
|
|
|
), |
|
|
|
@ -1250,61 +1287,36 @@ class MessageBubble extends GetView<AgentController> { |
|
|
|
child: Row( |
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
|
|
children: [ |
|
|
|
Text('距离:${modes[currentIndex]["distance"]}', |
|
|
|
style: |
|
|
|
const TextStyle(fontSize: 12, color: Colors.blue)), |
|
|
|
IconButton( |
|
|
|
icon: const Icon(Icons.turn_left, |
|
|
|
color: Colors.blue, size: 20), |
|
|
|
onPressed: () { |
|
|
|
controller.openNavigation(); |
|
|
|
}, |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
|
|
|
|
// 导航实时数据显示 |
|
|
|
if (navigation != null) |
|
|
|
Padding( |
|
|
|
padding: const EdgeInsets.all(8), |
|
|
|
child: Row( |
|
|
|
crossAxisAlignment: CrossAxisAlignment.center, |
|
|
|
children: [ |
|
|
|
// 圆形方向图标(如果有) |
|
|
|
if (navigation["directionIcon"] != "") |
|
|
|
Container( |
|
|
|
width: 32, |
|
|
|
height: 32, |
|
|
|
decoration: const BoxDecoration( |
|
|
|
shape: BoxShape.circle, |
|
|
|
color: Colors.blue, |
|
|
|
), |
|
|
|
child: ClipOval( |
|
|
|
child: Image.memory( |
|
|
|
base64Decode(navigation["directionIcon"]), |
|
|
|
fit: BoxFit.cover, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
const SizedBox(width: 8), |
|
|
|
// 文字信息 |
|
|
|
Expanded( |
|
|
|
child: Column( |
|
|
|
Column( |
|
|
|
crossAxisAlignment: CrossAxisAlignment.start, |
|
|
|
children: [ |
|
|
|
if (navigation["currentRoad"] != null) |
|
|
|
Text('当前道路:${navigation["currentRoad"]}', |
|
|
|
style: const TextStyle(fontSize: 12)), |
|
|
|
if (navigation["nextRoad"] != null) |
|
|
|
Text('下一道路:${navigation["nextRoad"]}', |
|
|
|
style: const TextStyle(fontSize: 12)), |
|
|
|
if (navigation["distanceText"] != null) |
|
|
|
Text('剩余距离:${navigation["distanceText"]}', |
|
|
|
style: const TextStyle(fontSize: 12)), |
|
|
|
Text('$destination', |
|
|
|
style: const TextStyle( |
|
|
|
fontSize: 12, color: Colors.black)), |
|
|
|
Text( |
|
|
|
'${formatDistance(modes[currentIndex]["distance"])}-约${formatDuration(modes[currentIndex]["duration"])}', |
|
|
|
style: const TextStyle( |
|
|
|
fontSize: 15, color: Colors.blue)), |
|
|
|
if (navigation != null) |
|
|
|
Text( |
|
|
|
"导航中... ${navigation["distanceText"]}进入${navigation["nextRoad"]}", |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 12, |
|
|
|
color: |
|
|
|
const Color.fromARGB(255, 164, 164, 164))) |
|
|
|
], |
|
|
|
), |
|
|
|
CircleAvatar( |
|
|
|
backgroundColor: Colors.blue, |
|
|
|
radius: 18, |
|
|
|
child: IconButton( |
|
|
|
icon: const Icon(Icons.turn_left, |
|
|
|
color: Colors.white, size: 20), |
|
|
|
onPressed: () { |
|
|
|
controller.openNavigation(); |
|
|
|
}, |
|
|
|
), |
|
|
|
) |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
|