import 'package:flutter/material.dart'; import '../core/responsive/responsive.dart'; import 'package:lpinyin/lpinyin.dart'; import '../models/product.dart'; class SelectProductDialog extends StatefulWidget { final List products; final int? selectedId; const SelectProductDialog({ super.key, required this.products, this.selectedId, }); @override State createState() => _SelectProductDialogState(); } class _SelectProductDialogState extends State { final _searchCtrl = TextEditingController(); List _filtered = []; // 缓存每个商品的拼音索引,避免搜索时重复计算 late final Map _pinyinCache; @override void initState() { super.initState(); _pinyinCache = { for (final p in widget.products) p.id: _PinyinIndex.from(p), }; _filtered = widget.products; _searchCtrl.addListener(_onSearch); } @override void dispose() { _searchCtrl.dispose(); super.dispose(); } void _onSearch() { final q = _searchCtrl.text.trim().toLowerCase(); setState(() { if (q.isEmpty) { _filtered = widget.products; } else { _filtered = widget.products.where((p) { // 原文匹配(名称、规格、编码、条码) if (p.name.toLowerCase().contains(q)) return true; if (p.spec?.toLowerCase().contains(q) ?? false) return true; if (p.code.toLowerCase().contains(q)) return true; if (p.barcode?.toLowerCase().contains(q) ?? false) return true; // 拼音匹配(全拼 or 首字母) final idx = _pinyinCache[p.id]!; if (idx.fullPinyin.contains(q)) return true; if (idx.initials.contains(q)) return true; return false; }).toList(); } }); } @override Widget build(BuildContext context) { // 每行约 56px,窗口默认显示约 6 行,可滚动查看全部 const listHeight = 56.0 * 6; return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: SizedBox( width: context.dialogWidth(480), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 标题栏 Padding( padding: const EdgeInsets.fromLTRB(20, 16, 8, 0), child: Row( children: [ const Text('选择商品', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)), const Spacer(), IconButton( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.close, size: 20), visualDensity: VisualDensity.compact, ), ], ), ), // 搜索框 Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 8), child: TextField( controller: _searchCtrl, autofocus: true, decoration: InputDecoration( hintText: '搜索名称 / 规格 / 编码 / 拼音首字母...', prefixIcon: const Icon(Icons.search, size: 18), suffixIcon: _searchCtrl.text.isNotEmpty ? IconButton( onPressed: _searchCtrl.clear, icon: const Icon(Icons.clear, size: 16), visualDensity: VisualDensity.compact, ) : null, isDense: true, contentPadding: const EdgeInsets.symmetric(vertical: 10), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), ), ), ), const Divider(height: 1), // 商品列表(固定高度,可滚动) if (_filtered.isEmpty) const SizedBox( height: listHeight, child: Center( child: Text('无匹配商品', style: TextStyle(color: Colors.grey)), ), ) else SizedBox( height: listHeight, child: ListView.separated( itemCount: _filtered.length, separatorBuilder: (_, __) => const Divider(height: 1, indent: 16, endIndent: 16), itemBuilder: (context, i) { final p = _filtered[i]; final isSelected = p.id == widget.selectedId; return ListTile( dense: true, selected: isSelected, selectedTileColor: Colors.blue.shade50, title: Text( p.name, style: const TextStyle(fontSize: 13), ), subtitle: Text( [ if (p.spec != null && p.spec!.isNotEmpty) p.spec!, p.unit, if (p.code.isNotEmpty) '编码: ${p.code}', ].join(' · '), style: const TextStyle(fontSize: 12, color: Colors.grey), ), trailing: isSelected ? const Icon(Icons.check, color: Colors.blue, size: 18) : null, onTap: () => Navigator.pop(context, p), ); }, ), ), const SizedBox(height: 8), ], ), ), ); } } /// 每个商品的拼音索引:全拼 + 首字母,在 initState 时一次性计算并缓存 class _PinyinIndex { final String fullPinyin; // "longjingcha" final String initials; // "ljc" const _PinyinIndex({required this.fullPinyin, required this.initials}); factory _PinyinIndex.from(Product p) { // 拼接名称 + 规格作为拼音转换源 final text = '${p.name}${p.spec ?? ''}'; final full = PinyinHelper.getPinyinE(text, separator: '', defPinyin: '') .toLowerCase(); final init = PinyinHelper.getShortPinyin(text).toLowerCase(); return _PinyinIndex(fullPinyin: full, initials: init); } }