Update App.jsx via upload script - 2026-08-31 14:19:48

This commit is contained in:
mike 2026-08-31 14:20:02 +08:00
parent 8a034ffac8
commit e5b39eb4de
1 changed files with 51 additions and 5 deletions

56
App.jsx
View File

@ -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">