diff --git a/static/js/app.js b/static/js/app.js
index d395943..97a6202 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -3389,9 +3389,9 @@ function renderBrandManagementPagination(total,size,page){var el=document.getEle
function showBrandAdd(){showBrandModal('新增品牌','','');}
function showBrandEdit(brand){fetch('/api/brand-management/list?search='+encodeURIComponent(brand)+'&size=10').then(function(r){return r.json();}).then(function(res){var row=(res.data||[]).find(function(x){return x.brand===brand;});showBrandModal('编辑品牌',brand,row?row.category:'');}).catch(function(){showBrandModal('编辑品牌',brand,'');});}
function showBrandModal(title,brand,category){var old=document.getElementById('brandManageModal');if(old)old.remove();var overlay=document.createElement('div');overlay.id='brandManageModal';overlay.className='modal-overlay';overlay.style.display='flex';overlay.onclick=function(e){if(e.target===overlay){}};overlay.innerHTML='
';document.body.appendChild(overlay);document.getElementById('brandManageModalCategory').value=category||'';document.getElementById('brandManageName').focus();}
-function submitBrandModal(oldBrand){var name=(document.getElementById('brandManageName').value||'').trim(),category=document.getElementById('brandManageModalCategory').value||'';if(!name){alert('品牌名称不能为空');return;}var isEdit=!!oldBrand;fetch(isEdit?'/api/brand-management/update':'/api/brand-management/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(isEdit?{old_brand:oldBrand,new_brand:name,category:category}:{brand:name,category:category})}).then(function(r){return r.json();}).then(function(res){if(res.code!==0){alert(res.msg||'保存失败');return;}document.getElementById('brandManageModal').remove();loadBrandManagementData(window._brandManagePage||1);}).catch(function(){alert('网络异常,保存失败');});}
+function submitBrandModal(oldBrand){var name=(document.getElementById('brandManageName').value||'').trim(),category=document.getElementById('brandManageModalCategory').value||'';if(!name){alert('品牌名称不能为空');return;}var isEdit=!!oldBrand;fetch(isEdit?'/api/brand-management/update':'/api/brand-management/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(isEdit?{old_brand:oldBrand,new_brand:name,category:category}:{brand:name,category:category})}).then(function(r){return r.json();}).then(function(res){if(res.code!==0){alert(res.msg||'保存失败');return;}window._brandList=null;document.getElementById('brandManageModal').remove();loadBrandManagementData(window._brandManagePage||1);}).catch(function(){alert('网络异常,保存失败');});}
function saveBrandCategory(brand,category){fetch('/api/brand-management/update',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({old_brand:brand,new_brand:brand,category:category})}).then(function(r){return r.json();}).then(function(res){if(res.code!==0){alert(res.msg||'保存失败');}else{loadBrandManagementData(window._brandManagePage||1);}}).catch(function(){alert('网络异常,保存失败');});}
-function deleteBrand(brand){showCenteredConfirm('确认删除','确定删除品牌“'+brand+'”吗?删除后不可恢复;正在被产品使用的品牌将受到保护,无法删除。').then(function(ok){if(!ok)return;fetch('/api/brand-management/delete',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({brand:brand})}).then(function(r){return r.json();}).then(function(res){if(res.code!==0){showCenteredAlert('删除失败',res.msg||'删除失败');return;}showCenteredAlert('删除成功',res.msg||'品牌已删除');loadBrandManagementData(window._brandManagePage||1);}).catch(function(){showCenteredAlert('删除失败','网络异常,请稍后重试');});});}
+function deleteBrand(brand){showCenteredConfirm('确认删除','确定删除品牌“'+brand+'”吗?删除后不可恢复;正在被产品使用的品牌将受到保护,无法删除。').then(function(ok){if(!ok)return;fetch('/api/brand-management/delete',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({brand:brand})}).then(function(r){return r.json();}).then(function(res){if(res.code!==0){showCenteredAlert('删除失败',res.msg||'删除失败');return;}window._brandList=null;showCenteredAlert('删除成功',res.msg||'品牌已删除');loadBrandManagementData(window._brandManagePage||1);}).catch(function(){showCenteredAlert('删除失败','网络异常,请稍后重试');});});}
// ─── 类目管理页面 ───
function loadProductCategoryPage(container) {
@@ -28890,6 +28890,8 @@ function _buildProductEditForm(row) {
opts += '';
});
html += '';
+ } else if (f.key === 'brand') {
+ html += '';
} else if (f.supSearch) {
html += '';
} else {
@@ -28926,6 +28928,40 @@ window.editSupSearch = function(input) {
});
};
+// ─── 产品信息管理编辑弹窗:品牌模糊搜索 ───
+window.editBrandSearch = function(input, showAll) {
+ var dd = input && input.parentElement ? input.parentElement.querySelector('.pe-brand-dd') : null;
+ if (!dd) return;
+ window.loadBrandList(function(list) {
+ if (!document.body.contains(input)) return;
+ var keyword = showAll ? '' : String(input.value || '').trim().toLowerCase();
+ var matched = (list || []).filter(function(brand) {
+ return !keyword || String(brand).toLowerCase().indexOf(keyword) >= 0;
+ });
+ if (!matched.length) {
+ dd.innerHTML = '未找到匹配品牌
';
+ dd.style.display = 'block';
+ return;
+ }
+ dd.innerHTML = matched.slice(0, 50).map(function(brand) {
+ return '' + escHtml(brand) + '
';
+ }).join('');
+ dd.style.display = 'block';
+ });
+};
+
+window.selectEditBrand = function(brand) {
+ var input = document.getElementById('pe_brand');
+ if (input) input.value = brand || '';
+ window.hideEditBrandDropdown();
+};
+
+window.hideEditBrandDropdown = function() {
+ var modal = document.getElementById('productEditModal');
+ var dd = modal ? modal.querySelector('.pe-brand-dd') : null;
+ if (dd) dd.style.display = 'none';
+};
+
window.saveQuickEditProduct = function(productCode) {
var isNonProd = prodActiveTab === '非成品';
var skipFields = ['brand','procurement_follower','procurement_planner','platform','operator','developer','created_at'];
diff --git a/tests/test_product_brand_search.py b/tests/test_product_brand_search.py
new file mode 100644
index 0000000..3d746e4
--- /dev/null
+++ b/tests/test_product_brand_search.py
@@ -0,0 +1,28 @@
+"""产品信息管理编辑弹窗品牌模糊搜索回归测试。"""
+
+import unittest
+from pathlib import Path
+
+
+APP_JS = (Path(__file__).resolve().parents[1] / 'static/js/app.js').read_text(encoding='utf-8')
+
+
+class ProductBrandSearchTest(unittest.TestCase):
+ def test_quick_edit_brand_uses_searchable_input(self):
+ self.assertIn('id="pe_brand" type="text"', APP_JS)
+ self.assertIn('oninput="editBrandSearch(this)"', APP_JS)
+ self.assertIn('onfocus="editBrandSearch(this,true)"', APP_JS)
+ self.assertIn('class="pe-brand-dd"', APP_JS)
+
+ def test_brand_search_uses_shared_brand_list_and_fuzzy_match(self):
+ self.assertIn('window.editBrandSearch = function(input, showAll)', APP_JS)
+ self.assertIn('window.loadBrandList(function(list)', APP_JS)
+ self.assertIn("String(brand).toLowerCase().indexOf(keyword) >= 0", APP_JS)
+ self.assertIn('window.selectEditBrand = function(brand)', APP_JS)
+
+ def test_brand_changes_invalidate_search_cache(self):
+ self.assertGreaterEqual(APP_JS.count('window._brandList=null'), 2)
+
+
+if __name__ == '__main__':
+ unittest.main()