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 [productDraft, setProductDraft] = useState(null);
|
||||||
const [productSearch, setProductSearch] = useState('');
|
const [productSearch, setProductSearch] = useState('');
|
||||||
const [productCategoryFilter, setProductCategoryFilter] = useState('all');
|
const [productCategoryFilter, setProductCategoryFilter] = useState('all');
|
||||||
|
const [productTypeFilter, setProductTypeFilter] = useState('all');
|
||||||
|
const [productBrandFilter, setProductBrandFilter] = useState('all');
|
||||||
const [aiImportOpen, setAiImportOpen] = useState(false);
|
const [aiImportOpen, setAiImportOpen] = useState(false);
|
||||||
const [aiImportBusy, setAiImportBusy] = useState(false);
|
const [aiImportBusy, setAiImportBusy] = useState(false);
|
||||||
const [aiImportError, setAiImportError] = useState('');
|
const [aiImportError, setAiImportError] = useState('');
|
||||||
|
|
@ -2247,9 +2249,29 @@ function MainApp({ username, onLogout }) {
|
||||||
const vipCustomers = useMemo(() => customers.filter(c => c.isVip), [customers]);
|
const vipCustomers = useMemo(() => customers.filter(c => c.isVip), [customers]);
|
||||||
const normalCustomers = 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(() => {
|
const filteredProducts = useMemo(() => {
|
||||||
let list = products;
|
let list = productsInCategoryAndType;
|
||||||
if (productCategoryFilter !== 'all') list = list.filter(p => p.category === productCategoryFilter);
|
if (productBrandFilter !== 'all') list = list.filter(p => p.brand === productBrandFilter);
|
||||||
if (productSearch.trim()) {
|
if (productSearch.trim()) {
|
||||||
const q = productSearch.trim().toLowerCase();
|
const q = productSearch.trim().toLowerCase();
|
||||||
list = list.filter(p =>
|
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 || ''));
|
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(() => {
|
const filteredSuppliers = useMemo(() => {
|
||||||
let list = suppliers;
|
let list = suppliers;
|
||||||
|
|
@ -3463,11 +3495,25 @@ function MainApp({ username, onLogout }) {
|
||||||
<div>
|
<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="search-box"><Search size={14} color="var(--ink-soft)" /><input placeholder="搜索型号 / 品牌 / 类别" value={productSearch} onChange={e => setProductSearch(e.target.value)} /></div>
|
||||||
<div className="field">
|
<div className="field">
|
||||||
<select className="select" value={productCategoryFilter} onChange={e => setProductCategoryFilter(e.target.value)}>
|
<select className="select" value={productCategoryFilter} onChange={e => handleProductCategoryFilterChange(e.target.value)}>
|
||||||
<option value="all">全部类别</option>
|
<option value="all">全部大类</option>
|
||||||
{productCategories.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
|
{productCategories.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</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 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>
|
<button className="btn" style={{ width: '100%', justifyContent: 'center', marginBottom: 12 }} onClick={openAiImport}><Sparkles size={14} />AI导入(上传PDF/图片自动建档)</button>
|
||||||
<div className="panel">
|
<div className="panel">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue