Update WBBR frontend template collection
This commit is contained in:
3
app/simple-list/SimpleList.module.css
Normal file
3
app/simple-list/SimpleList.module.css
Normal file
File diff suppressed because one or more lines are too long
105
app/simple-list/page.tsx
Normal file
105
app/simple-list/page.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import data from "../data/content-templates.json";
|
||||
import styles from "./SimpleList.module.css";
|
||||
|
||||
const categories = ["全部", "世界观", "制作资料", "角色", "音乐", "品牌", "运营"];
|
||||
const statusStyles: Record<string, string> = {
|
||||
"已发布": "statusPublished",
|
||||
"评审中": "statusReview",
|
||||
"草稿": "statusDraft",
|
||||
"已归档": "statusArchived",
|
||||
};
|
||||
|
||||
export default function SimpleListPage() {
|
||||
const [query, setQuery] = useState("");
|
||||
const [category, setCategory] = useState("全部");
|
||||
const [status, setStatus] = useState("全部状态");
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const [favorites, setFavorites] = useState<string[]>(data.listItems.filter((item) => item.favorite).map((item) => item.id));
|
||||
const [toast, setToast] = useState("");
|
||||
const [compact, setCompact] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const saved = window.localStorage.getItem("orbit-simple-list-favorites");
|
||||
if (saved) {
|
||||
try { setFavorites(JSON.parse(saved)); } catch { /* use JSON defaults */ }
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.localStorage.setItem("orbit-simple-list-favorites", JSON.stringify(favorites));
|
||||
}, [favorites]);
|
||||
|
||||
const items = useMemo(() => data.listItems.filter((item) => {
|
||||
const matchQuery = `${item.title}${item.description}${item.id}${item.tags.join("")}`.toLowerCase().includes(query.toLowerCase());
|
||||
return matchQuery && (category === "全部" || item.category === category) && (status === "全部状态" || item.status === status);
|
||||
}), [query, category, status]);
|
||||
|
||||
const notify = (message: string) => {
|
||||
setToast(message);
|
||||
window.setTimeout(() => setToast(""), 2000);
|
||||
};
|
||||
|
||||
const toggleAll = () => setSelected(selected.length === items.length ? [] : items.map((item) => item.id));
|
||||
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<header className={styles.topbar}>
|
||||
<Link className={styles.brand} href="/"><span>O</span><strong>Orbit Docs</strong></Link>
|
||||
<nav><Link className={styles.active} href="/simple-list">简单列表</Link><Link href="/character">角色详情</Link><Link href="/markdown">Markdown 编辑器</Link></nav>
|
||||
<div className={styles.topActions}><button onClick={() => notify("帮助中心为模板占位")}>?</button><span>林</span></div>
|
||||
</header>
|
||||
|
||||
<main className={styles.content}>
|
||||
<div className={styles.heading}>
|
||||
<div><p>CONTENT LIBRARY</p><h1>内容资料库</h1><span>轻量、清晰的通用列表模板,适合文档、项目或数据记录。</span></div>
|
||||
<button className={styles.createButton} onClick={() => notify("新建资料表单为模板占位")}>+ 新建资料</button>
|
||||
</div>
|
||||
|
||||
<section className={styles.summary}>
|
||||
<div><span className={styles.iconPurple}>▤</span><p><small>全部资料</small><strong>{data.listItems.length}</strong></p></div>
|
||||
<div><span className={styles.iconGreen}>✓</span><p><small>已发布</small><strong>{data.listItems.filter((item) => item.status === "已发布").length}</strong></p></div>
|
||||
<div><span className={styles.iconOrange}>◷</span><p><small>等待评审</small><strong>{data.listItems.filter((item) => item.status === "评审中").length}</strong></p></div>
|
||||
<div><span className={styles.iconBlue}>★</span><p><small>我的收藏</small><strong>{favorites.length}</strong></p></div>
|
||||
</section>
|
||||
|
||||
<section className={styles.listPanel}>
|
||||
<div className={styles.categoryTabs}>
|
||||
{categories.map((item) => <button key={item} className={category === item ? styles.tabActive : ""} onClick={() => setCategory(item)}>{item}</button>)}
|
||||
</div>
|
||||
<div className={styles.toolbar}>
|
||||
<label className={styles.search}><span>⌕</span><input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="搜索标题、编号或标签" />{query && <button onClick={() => setQuery("")}>×</button>}</label>
|
||||
<select value={status} onChange={(event) => setStatus(event.target.value)}><option>全部状态</option><option>已发布</option><option>评审中</option><option>草稿</option><option>已归档</option></select>
|
||||
<button className={styles.filterButton} onClick={() => { setQuery(""); setCategory("全部"); setStatus("全部状态"); }}>清除筛选</button>
|
||||
<div className={styles.viewToggle}><button className={!compact ? styles.viewActive : ""} onClick={() => setCompact(false)}>☷</button><button className={compact ? styles.viewActive : ""} onClick={() => setCompact(true)}>☰</button></div>
|
||||
</div>
|
||||
|
||||
{selected.length > 0 && <div className={styles.bulkBar}><strong>已选择 {selected.length} 项</strong><button onClick={() => notify("已添加到收藏")}>收藏</button><button onClick={() => notify("已移动到归档")}>归档</button><button onClick={() => setSelected([])}>取消选择</button></div>}
|
||||
|
||||
<div className={`${styles.list} ${compact ? styles.compact : ""}`}>
|
||||
<div className={styles.listHeader}><input type="checkbox" checked={items.length > 0 && selected.length === items.length} onChange={toggleAll} aria-label="全选" /><span>资料名称</span><span>分类</span><span>状态</span><span>负责人</span><span>更新时间</span><span /></div>
|
||||
{items.map((item) => {
|
||||
const checked = selected.includes(item.id);
|
||||
const favorite = favorites.includes(item.id);
|
||||
return <article key={item.id} className={checked ? styles.rowSelected : ""}>
|
||||
<input type="checkbox" checked={checked} onChange={() => setSelected((current) => checked ? current.filter((id) => id !== item.id) : [...current, item.id])} aria-label={`选择 ${item.title}`} />
|
||||
<button className={styles.titleCell} onClick={() => notify(`打开:${item.title}`)}><span>{item.category.slice(0, 1)}</span><div><strong>{item.title}</strong><p>{item.description}</p><small>{item.id} {item.tags.map((tag) => <i key={tag}>#{tag}</i>)}</small></div></button>
|
||||
<span className={styles.category}>{item.category}</span>
|
||||
<span className={`${styles.status} ${styles[statusStyles[item.status]]}`}><i />{item.status}</span>
|
||||
<span className={styles.owner}><i>{item.initial}</i>{item.owner}</span>
|
||||
<time>{item.updated}</time>
|
||||
<div className={styles.rowActions}><button className={favorite ? styles.favorite : ""} onClick={() => setFavorites((current) => favorite ? current.filter((id) => id !== item.id) : [...current, item.id])}>★</button><button onClick={() => notify("更多操作:复制、移动、归档")}>•••</button></div>
|
||||
</article>;
|
||||
})}
|
||||
{items.length === 0 && <div className={styles.empty}><span>⌕</span><strong>没有找到匹配资料</strong><p>调整关键词或筛选条件后再试试。</p><button onClick={() => { setQuery(""); setCategory("全部"); setStatus("全部状态"); }}>重置筛选</button></div>}
|
||||
</div>
|
||||
<footer className={styles.pagination}><span>显示 {items.length} 条,共 {data.listItems.length} 条</span><div><button disabled>←</button><button className={styles.pageActive}>1</button><button>2</button><button>→</button></div></footer>
|
||||
</section>
|
||||
</main>
|
||||
{toast && <div className={styles.toast}>✓ {toast}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user