import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import '../../core/config/app_config.dart'; import '../../core/theme/app_theme.dart'; class PublicProductScreen extends StatefulWidget { final String publicId; const PublicProductScreen({super.key, required this.publicId}); @override State createState() => _PublicProductScreenState(); } class _PublicProductScreenState extends State { Map? _data; String? _error; bool _loading = true; final _dio = Dio(BaseOptions( connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 15), )); @override void initState() { super.initState(); _load(); } Future _load() async { setState(() { _loading = true; _error = null; }); try { final url = '${AppConfig.apiBaseUrl}/public/products/${widget.publicId}'; final resp = await _dio.get(url); setState(() { _data = (resp.data as Map)['data'] as Map; _loading = false; }); } catch (e) { setState(() { _error = e.toString(); _loading = false; }); } } @override Widget build(BuildContext context) { if (_loading) { return const Scaffold(body: Center(child: CircularProgressIndicator())); } if (_error != null || _data == null) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, size: 40, color: AppTheme.danger), const SizedBox(height: 12), const Text('商品不存在或已下架'), const SizedBox(height: 12), ElevatedButton(onPressed: _load, child: const Text('重试')), ], ), ), ); } final d = _data!; final images = (d['images'] as List? ?? []) .cast>(); final name = d['name'] as String? ?? ''; final series = d['series'] as String? ?? ''; final spec = d['spec'] as String? ?? ''; final brand = d['brand'] as String? ?? ''; final unit = d['unit'] as String? ?? ''; final description = d['description'] as String? ?? ''; return Scaffold( body: CustomScrollView( slivers: [ // Image carousel or placeholder SliverToBoxAdapter( child: images.isEmpty ? Container( height: 240, color: const Color(0xFFF5F5F5), child: const Center( child: Icon(Icons.wine_bar, size: 80, color: Color(0xFFCCCCCC)), ), ) : SizedBox( height: 300, child: PageView( children: images.map((img) { final url = AppConfig.baseUrl + (img['url'] as String); return Image.network( url, fit: BoxFit.cover, errorBuilder: (_, __, ___) => Container( color: const Color(0xFFF5F5F5), child: const Icon(Icons.broken_image, size: 48, color: Color(0xFFCCCCCC)), ), ); }).toList(), ), ), ), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(name, style: const TextStyle( fontSize: 22, fontWeight: FontWeight.w700)), const SizedBox(height: 6), if (series.isNotEmpty || spec.isNotEmpty) Text( [if (series.isNotEmpty) series, if (spec.isNotEmpty) spec] .join(' · '), style: const TextStyle( fontSize: 15, color: AppTheme.textSecondary), ), if (brand.isNotEmpty) ...[ const SizedBox(height: 4), Text('品牌:$brand', style: const TextStyle( fontSize: 13, color: AppTheme.textSecondary)), ], if (unit.isNotEmpty) ...[ const SizedBox(height: 4), Text('单位:$unit', style: const TextStyle( fontSize: 13, color: AppTheme.textSecondary)), ], if (description.isNotEmpty) ...[ const SizedBox(height: 20), const Text('关于这款酒', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600)), const SizedBox(height: 8), Text(description, style: const TextStyle( fontSize: 14, height: 1.7, color: AppTheme.textPrimary)), ], const SizedBox(height: 40), const Center( child: Text('酒库管理系统', style: TextStyle( fontSize: 12, color: AppTheme.textSecondary)), ), ], ), ), ), ], ), ); } }