192 lines
10 KiB
JavaScript
192 lines
10 KiB
JavaScript
// 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;
|