diff --git a/yuthon_base/models/ir_ui_menu.py b/yuthon_base/models/ir_ui_menu.py index eeaafe667..a8d97f6dd 100644 --- a/yuthon_base/models/ir_ui_menu.py +++ b/yuthon_base/models/ir_ui_menu.py @@ -1,65 +1,43 @@ # -*- coding: utf-8 -*- from odoo import api, fields, models, _ +from odoo.osv import expression import pypinyin -import re class IrUiMenu(models.Model): _inherit = 'ir.ui.menu' - name_pinyin = fields.Char( - string='菜单名拼音', - compute='_compute_name_pinyin', - store=True, - index=True, - ) - - @api.depends('name') - def _compute_name_pinyin(self): - for menu in self: - if menu.name: - # 全拼:如 "通知公告" → "tongzhigonggao" - full_pinyin = ''.join(pypinyin.lazy_pinyin(menu.name)).lower() - # 首字母:如 "通知公告" → "tzgg" - first_letters = ''.join([ - p[0] for p in pypinyin.pinyin(menu.name) - ]).lower() - menu.name_pinyin = f"{full_pinyin} {first_letters}" - else: - menu.name_pinyin = False - def name_search(self, name='', args=None, operator='ilike', limit=100): - """ - 重写 name_search 支持全拼/首字母模糊搜索菜单 - """ + """支持全拼/首字母模糊搜索菜单(Ctrl+K)""" if args is None: args = [] - if name and operator in ('ilike', 'like', '=', '=like'): - # 清理输入:去掉空格、转小写 - search_term = name.strip().lower().replace(' ', '') - if not search_term: - return super().name_search(name=name, args=args, operator=operator, limit=limit) + if not name or operator not in ('ilike', 'like', '=', '=like'): + return super().name_search(name=name, args=args, operator=operator, limit=limit) - # 生成搜索词的拼音和首字母 - search_pinyin = ''.join(pypinyin.lazy_pinyin(search_term)).lower() - search_first = ''.join([p[0] for p in pypinyin.pinyin(search_term)]).lower() + search_term = name.strip().lower().replace(' ', '') + if not search_term: + return super().name_search(name=name, args=args, operator=operator, limit=limit) - # 构建拼音搜索域:匹配 name_pinyin 字段 - pinyin_domain = [ - '|', - ('name_pinyin', 'ilike', search_term), # 直接搜索词 - ('name_pinyin', 'ilike', search_pinyin), # 全拼搜索 - ] - # 如果首字母不同,也加上 - if search_first != search_pinyin: - pinyin_domain = ['|'] + pinyin_domain + [('name_pinyin', 'ilike', search_first)] + # 生成搜索词的拼音和首字母 + search_pinyin = ''.join(pypinyin.lazy_pinyin(search_term)).lower() + search_first = ''.join([p[0] for p in pypinyin.pinyin(search_term)]).lower() - # 组合:原名搜索 OR 拼音搜索 - full_domain = ['|'] + expression.AND([args, [('name', operator, name)]]) + expression.AND([args, pinyin_domain]) + # 原名搜索 + name_domain = expression.AND([args, [('name', operator, name)]]) + name_ids = self.search(name_domain, limit=limit).ids - # 去重:用 browse 把结果合并(优先按原名匹配排序) - name_results = self.search(expression.AND([args, [('name', operator, name)]]), limit=limit) - pinyin_results = self.search(expression.AND([args, pinyin_domain]), limit=limit) - all_ids = list(dict.fromkeys(name_results.ids + pinyin_results.ids))[:limit] - return self.browse(all_ids).name_get() - return super().name_search(name=name, args=args, operator=operator, limit=limit) + # 拼音搜索:拿到所有菜单,在 Python 层过滤 + all_menus = self.search(args, limit=limit * 3) + pinyin_ids = [] + for menu in all_menus: + if not menu.name: + continue + menu_pinyin = ''.join(pypinyin.lazy_pinyin(menu.name)).lower() + menu_first = ''.join([p[0] for p in pypinyin.pinyin(menu.name)]).lower() + search_key = f"{menu_pinyin} {menu_first}" + if search_term in search_key or search_pinyin in search_key or search_first in search_key: + pinyin_ids.append(menu.id) + + # 合并,原名匹配优先 + all_ids = list(dict.fromkeys(name_ids + pinyin_ids))[:limit] + return self.browse(all_ids).name_get()