Files
ewoooc/MOMO Pro/app/page-products.jsx

192 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// EwoooC - 商品列表(對應截圖:價格 / 昨日漲跌 / 週漲跌 / 上次更新 / 上架時間 / 點擊彈窗)
const ChangeCell = ({ value }) => {
if (value == null) return <span style={{ color: 'var(--momo-text-tertiary)', fontFamily: 'var(--momo-font-family-mono)' }}></span>;
if (value === 0) return <span style={{ color: 'var(--momo-text-tertiary)', fontFamily: 'var(--momo-font-family-mono)' }}>0</span>;
const up = value > 0;
return (
<span className="momo-mono" style={{
display: 'inline-flex', alignItems: 'center', gap: 4,
color: up ? 'var(--momo-danger)' : 'var(--momo-success)',
fontWeight: 600,
}}>
<span style={{ fontSize: 9 }}>{up ? '▲' : '▼'}</span>
{up ? '+' : ''}{value.toLocaleString()}
</span>
);
};
const ProductsPage = ({ density = 'comfortable', cardStyle = 'flat', buttonStyle = 'primary', onEditProduct }) => {
const [search, setSearch] = React.useState('');
const [category, setCategory] = React.useState('all');
const D = EWOOOC_DATA;
const cats = ['all', ...new Set(D.products.map(p => p.category))];
const filtered = D.products.filter(p => {
if (category !== 'all' && p.category !== category) return false;
if (search && !p.name.includes(search) && !String(p.id).toLowerCase().includes(search.toLowerCase())) return false;
return true;
});
const rowPad = density === 'compact' ? '10px 16px' : '14px 16px';
return (
<div>
<PageHeader
title={<>商品列表 <span className="momo-mono" style={{ fontSize: 14, color: 'var(--momo-text-tertiary)', marginLeft: 12, letterSpacing: '0.04em' }}>商品監控</span></>}
subtitle={`監控 ${D.monitorStats.total.toLocaleString()} 件商品 · 點擊任一列查看 30 天價格走勢`}
actions={
<>
<Button variant="secondary" size="md" icon="upload">批次匯入</Button>
<Button variant="primary" size="md" icon="plus">新增監控</Button>
</>
}
/>
<Card cardStyle="flat" padding={false} style={{ background: 'var(--momo-bg-surface)', border: '1px solid var(--momo-border-light)', borderRadius: 4 }}>
{/* Toolbar */}
<div style={{
padding: '14px 20px',
display: 'flex', alignItems: 'center', gap: 10,
borderBottom: '1px solid var(--momo-border-light)',
flexWrap: 'wrap',
background: 'var(--momo-bg-paper)',
}}>
<div style={{ flex: 1, minWidth: 200, maxWidth: 320 }}>
<Input icon="search" placeholder="搜尋商品名稱、編號…" value={search} onChange={e => setSearch(e.target.value)} size="sm" />
</div>
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
{cats.slice(0, 6).map(c => (
<button key={c} onClick={() => setCategory(c)} style={{
padding: '6px 12px',
fontSize: 12, fontWeight: 500,
fontFamily: 'var(--momo-font-family-mono)',
borderRadius: 2,
background: category === c ? 'var(--momo-accent)' : 'transparent',
color: category === c ? '#faf7f0' : 'var(--momo-text-secondary)',
border: `1px solid ${category === c ? 'var(--momo-accent)' : 'var(--momo-border-light)'}`,
transition: 'var(--momo-transition-base)',
}}>{c === 'all' ? '全部' : c}</button>
))}
</div>
<div style={{ flex: 1 }} />
<span className="momo-mono" style={{ fontSize: 11, color: 'var(--momo-text-tertiary)' }}>
{filtered.length} / {D.products.length}
</span>
</div>
{/* Table */}
<div style={{ overflowX: 'auto' }}>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 'var(--momo-font-size-sm)' }}>
<thead>
<tr style={{ background: 'var(--momo-bg-paper)', borderBottom: '1px solid var(--momo-border-light)' }}>
{[
{ label: '編號', sub: '商品編號', align: 'left', w: 130 },
{ label: '分類', sub: '商品分類', align: 'left', w: 100 },
{ label: '商品', sub: '商品名稱', align: 'left' },
{ label: '售價', sub: '目前價格', align: 'right', w: 110 },
{ label: '昨日', sub: '昨日漲跌', align: 'right', w: 100 },
{ label: '本週', sub: '週漲跌', align: 'right', w: 100 },
{ label: '更新', sub: '上次更新', align: 'right', w: 110 },
{ label: '上架', sub: '上架時間', align: 'right', w: 110 },
{ label: '', sub: '', align: 'right', w: 50 },
].map((h, i) => (
<th key={i} style={{
padding: '10px 16px',
textAlign: h.align,
width: h.w,
fontSize: 10, fontWeight: 600,
fontFamily: 'var(--momo-font-family-mono)',
color: 'var(--momo-text-tertiary)',
letterSpacing: '0.06em',
whiteSpace: 'nowrap',
}}>
<div>{h.label}</div>
{h.sub && <div style={{ fontFamily: 'var(--momo-font-family-base)', fontSize: 11, color: 'var(--momo-text-secondary)', fontWeight: 500, letterSpacing: 0, marginTop: 1 }}>{h.sub}</div>}
</th>
))}
</tr>
</thead>
<tbody>
{filtered.map((p, idx) => (
<tr key={p.id}
onClick={() => onEditProduct && onEditProduct(p)}
style={{
borderTop: idx === 0 ? 'none' : '1px solid var(--momo-border-light)',
cursor: 'pointer',
transition: 'var(--momo-transition-base)',
}}
onMouseEnter={e => e.currentTarget.style.background = 'var(--momo-bg-paper)'}
onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
<td style={{ padding: rowPad, fontFamily: 'var(--momo-font-family-mono)', fontSize: 12, color: 'var(--momo-text-link)', fontWeight: 600 }}>
#{p.id}
</td>
<td style={{ padding: rowPad }}>
<Tag tone="earth" size="sm">{p.category}</Tag>
</td>
<td style={{ padding: rowPad }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<div style={{
width: 36, height: 36, borderRadius: 4,
background: 'var(--momo-bg-subtle)', border: '1px solid var(--momo-border-light)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 18, flexShrink: 0,
}}>{p.emoji}</div>
<span style={{ fontSize: 14, fontWeight: 500, color: 'var(--momo-text-primary)', display: '-webkit-box', WebkitLineClamp: 1, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>
{p.name}
</span>
</div>
</td>
<td style={{ padding: rowPad, textAlign: 'right', fontFamily: 'var(--momo-font-family-mono)', fontSize: 15, fontWeight: 700, color: 'var(--momo-text-primary)' }}>
${p.price.toLocaleString()}
</td>
<td style={{ padding: rowPad, textAlign: 'right' }}>
<ChangeCell value={p.yesterdayChange} />
</td>
<td style={{ padding: rowPad, textAlign: 'right' }}>
<ChangeCell value={p.weekChange} />
</td>
<td style={{ padding: rowPad, textAlign: 'right', fontFamily: 'var(--momo-font-family-mono)', fontSize: 12, color: 'var(--momo-text-secondary)' }}>
{p.updatedAt}
</td>
<td style={{ padding: rowPad, textAlign: 'right', fontFamily: 'var(--momo-font-family-mono)', fontSize: 12, color: 'var(--momo-text-secondary)' }}>
{p.listedAt}
</td>
<td style={{ padding: rowPad, textAlign: 'right' }}>
<span style={{ color: 'var(--momo-text-tertiary)' }}>
<Icon name="chevronRight" size={14} />
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div style={{
padding: '12px 20px',
borderTop: '1px solid var(--momo-border-light)',
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
background: 'var(--momo-bg-paper)',
}}>
<span className="momo-mono" style={{ fontSize: 11, color: 'var(--momo-text-tertiary)' }}>
顯示 1{filtered.length} {D.monitorStats.total.toLocaleString()}
</span>
<div style={{ display: 'flex', gap: 6 }}>
<button style={{ padding: '4px 10px', fontSize: 11, fontFamily: 'var(--momo-font-family-mono)', border: '1px solid var(--momo-border)', borderRadius: 2, color: 'var(--momo-text-secondary)' }}> 上一頁</button>
<button style={{ padding: '4px 10px', fontSize: 11, fontFamily: 'var(--momo-font-family-mono)', border: '1px solid var(--momo-accent)', borderRadius: 2, background: 'var(--momo-accent)', color: '#faf7f0', fontWeight: 600 }}>1</button>
<button style={{ padding: '4px 10px', fontSize: 11, fontFamily: 'var(--momo-font-family-mono)', border: '1px solid var(--momo-border)', borderRadius: 2, color: 'var(--momo-text-secondary)' }}>2</button>
<button style={{ padding: '4px 10px', fontSize: 11, fontFamily: 'var(--momo-font-family-mono)', border: '1px solid var(--momo-border)', borderRadius: 2, color: 'var(--momo-text-secondary)' }}>3</button>
<button style={{ padding: '4px 10px', fontSize: 11, fontFamily: 'var(--momo-font-family-mono)', border: '1px solid var(--momo-border)', borderRadius: 2, color: 'var(--momo-text-secondary)' }}>下一頁 </button>
</div>
</div>
</Card>
</div>
);
};
window.ProductsPage = ProductsPage;