From 6fc6b526318e257554accd4c04d5b56dca09a1bb Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Thu, 30 Apr 2026 20:16:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(client):=20=E6=90=9C=E7=B4=A2=E9=80=89?= =?UTF-8?q?=E9=A1=B9=E6=94=AF=E6=8C=81=E6=8B=BC=E9=9F=B3=E5=92=8C=E9=A6=96?= =?UTF-8?q?=E5=AD=97=E6=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使用 lpinyin 预计算每个选项的全拼和首字母, 输入 wl 可匹配「五粮液」,输入 maotai 可匹配「茅台」。 Co-Authored-By: Claude Sonnet 4.6 --- .../lib/widgets/searchable_option_field.dart | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/client/lib/widgets/searchable_option_field.dart b/client/lib/widgets/searchable_option_field.dart index 977491e..4b5ccc5 100644 --- a/client/lib/widgets/searchable_option_field.dart +++ b/client/lib/widgets/searchable_option_field.dart @@ -1,11 +1,27 @@ import 'package:flutter/material.dart'; +import 'package:lpinyin/lpinyin.dart'; import '../core/theme/app_theme.dart'; class OptionItem { final int id; final String name; final String? code; - const OptionItem({required this.id, required this.name, this.code}); + // 拼音索引,构造时预计算 + late final String _fullPinyin; + late final String _initials; + + OptionItem({required this.id, required this.name, this.code}) { + _fullPinyin = PinyinHelper.getPinyinE(name, separator: '', defPinyin: '').toLowerCase(); + _initials = PinyinHelper.getShortPinyin(name).toLowerCase(); + } + + bool matches(String kw) { + final k = kw.toLowerCase(); + return name.toLowerCase().contains(k) || + (_fullPinyin.isNotEmpty && _fullPinyin.contains(k)) || + (_initials.isNotEmpty && _initials.contains(k)) || + (code?.toLowerCase().contains(k) ?? false); + } } /// 点击后弹出搜索对话框的下拉选择框 @@ -112,9 +128,7 @@ class _SearchDialogState extends State<_SearchDialog> { Widget build(BuildContext context) { final filtered = _keyword.isEmpty ? widget.options - : widget.options.where((o) => - o.name.toLowerCase().contains(_keyword.toLowerCase()) || - (o.code?.toLowerCase().contains(_keyword.toLowerCase()) ?? false)).toList(); + : widget.options.where((o) => o.matches(_keyword)).toList(); return AlertDialog( title: Text(widget.title, style: const TextStyle(fontSize: 16)),