Update App.jsx via upload script - 2026-08-31 14:19:48
This commit is contained in:
parent
8a034ffac8
commit
e5b39eb4de
56
App.jsx
56
App.jsx
|
|
@ -2096,6 +2096,8 @@ function MainApp({ username, onLogout }) {
|
|||
const [productDraft, setProductDraft] = useState(null);
|
||||
const [productSearch, setProductSearch] = useState('');
|
||||
const [productCategoryFilter, setProductCategoryFilter] = useState('all');
|
||||
const [productTypeFilter, setProductTypeFilter] = useState('all');
|
||||
const [productBrandFilter, setProductBrandFilter] = useState('all');
|
||||
const [aiImportOpen, setAiImportOpen] = useState(false);
|
||||
const [aiImportBusy, setAiImportBusy] = useState(false);
|
||||
const [aiImportError, setAiImportError] = useState('');
|
||||
|
|
@ -2247,9 +2249,29 @@ function MainApp({ username, onLogout }) {
|
|||
const vipCustomers = useMemo(() => customers.filter(c => c.isVip), [customers]);
|
||||
const normalCustomers = useMemo(() => customers.filter(c => !c.isVip), [customers]);
|
||||
|
||||
const productsInCategory = useMemo(() => (
|
||||
productCategoryFilter === 'all' ? products : products.filter(p => p.category === productCategoryFilter)
|
||||
), [products, productCategoryFilter]);
|
||||
|
||||
// 可选的"类别"(细分品类,比如高频塔式UPS/工频UPS)跟着"大类"筛选联动收窄;
|
||||
// 可选的"品牌"再跟着"大类+类别"联动收窄,一层层缩小范围
|
||||
const availableProductTypes = useMemo(() => {
|
||||
const set = new Set(productsInCategory.map(p => p.type).filter(Boolean));
|
||||
return [...set].sort();
|
||||
}, [productsInCategory]);
|
||||
|
||||
const productsInCategoryAndType = useMemo(() => (
|
||||
productTypeFilter === 'all' ? productsInCategory : productsInCategory.filter(p => p.type === productTypeFilter)
|
||||
), [productsInCategory, productTypeFilter]);
|
||||
|
||||
const availableProductBrands = useMemo(() => {
|
||||
const set = new Set(productsInCategoryAndType.map(p => p.brand).filter(Boolean));
|
||||
return [...set].sort();
|
||||
}, [productsInCategoryAndType]);
|
||||
|
||||
const filteredProducts = useMemo(() => {
|
||||
let list = products;
|
||||
if (productCategoryFilter !== 'all') list = list.filter(p => p.category === productCategoryFilter);
|
||||
let list = productsInCategoryAndType;
|
||||
if (productBrandFilter !== 'all') list = list.filter(p => p.brand === productBrandFilter);
|
||||
if (productSearch.trim()) {
|
||||
const q = productSearch.trim().toLowerCase();
|
||||
list = list.filter(p =>
|
||||
|
|
@ -2259,7 +2281,17 @@ function MainApp({ username, onLogout }) {
|
|||
);
|
||||
}
|
||||
return [...list].sort((a, b) => categoryBreadcrumb(a, productCategories).localeCompare(categoryBreadcrumb(b, productCategories)) || (a.name || '').localeCompare(b.name || ''));
|
||||
}, [products, productSearch, productCategoryFilter, productCategories]);
|
||||
}, [productsInCategoryAndType, productBrandFilter, productSearch, productCategories]);
|
||||
|
||||
function handleProductCategoryFilterChange(catId) {
|
||||
setProductCategoryFilter(catId);
|
||||
setProductTypeFilter('all');
|
||||
setProductBrandFilter('all');
|
||||
}
|
||||
function handleProductTypeFilterChange(type) {
|
||||
setProductTypeFilter(type);
|
||||
setProductBrandFilter('all');
|
||||
}
|
||||
|
||||
const filteredSuppliers = useMemo(() => {
|
||||
let list = suppliers;
|
||||
|
|
@ -3463,11 +3495,25 @@ function MainApp({ username, onLogout }) {
|
|||
<div>
|
||||
<div className="search-box"><Search size={14} color="var(--ink-soft)" /><input placeholder="搜索型号 / 品牌 / 类别" value={productSearch} onChange={e => setProductSearch(e.target.value)} /></div>
|
||||
<div className="field">
|
||||
<select className="select" value={productCategoryFilter} onChange={e => setProductCategoryFilter(e.target.value)}>
|
||||
<option value="all">全部类别</option>
|
||||
<select className="select" value={productCategoryFilter} onChange={e => handleProductCategoryFilterChange(e.target.value)}>
|
||||
<option value="all">全部大类</option>
|
||||
{productCategories.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div className="grid-2">
|
||||
<div className="field">
|
||||
<select className="select" value={productTypeFilter} onChange={e => handleProductTypeFilterChange(e.target.value)}>
|
||||
<option value="all">全部类别</option>
|
||||
{availableProductTypes.map(t => <option key={t} value={t}>{t}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<select className="select" value={productBrandFilter} onChange={e => setProductBrandFilter(e.target.value)}>
|
||||
<option value="all">全部品牌</option>
|
||||
{availableProductBrands.map(b => <option key={b} value={b}>{b}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button className="btn btn-primary" style={{ width: '100%', justifyContent: 'center', marginBottom: 8 }} onClick={startNewProduct}><Plus size={14} />新建产品档案</button>
|
||||
<button className="btn" style={{ width: '100%', justifyContent: 'center', marginBottom: 12 }} onClick={openAiImport}><Sparkles size={14} />AI导入(上传PDF/图片自动建档)</button>
|
||||
<div className="panel">
|
||||
|
|
|
|||
Loading…
Reference in New Issue