feat: 格式从分组下沉到条目(v21)
- entry 新增 format(apps/posters/articles),老数据按根分组 view_mode 回填;seed 按分区写入 - 分组管理移除格式字段/显示,根分组只作容器 - 内容弹窗新增逐条格式选择,列表增加格式列;ItemEditor 保存时保留原格式 - 前台按 entry.format 渲染:卡片/分组页支持同组混合格式,详情按条目格式选模板 - 迁移后 live 20 应用 / 13 海报;cargo test 59 passed;Playwright 验证混合展示
This commit is contained in:
+19
-11
@@ -31,6 +31,7 @@ struct NodeRow {
|
||||
/// 条目行:与 `entry` 表的列一一对应
|
||||
struct EntryRow {
|
||||
key: String,
|
||||
format: String,
|
||||
name: String,
|
||||
description: Option<String>,
|
||||
icon: Option<String>,
|
||||
@@ -140,7 +141,7 @@ pub fn load_tree(conn: &Connection) -> Result<Value, StashError> {
|
||||
let entries = load_node_entries(conn, &node.key)?;
|
||||
let items = entries
|
||||
.iter()
|
||||
.map(|entry| item_for(entry, format, tags))
|
||||
.map(|entry| item_for(entry, tags))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let child_values = children
|
||||
@@ -338,12 +339,12 @@ fn build_section(
|
||||
if section.allow_groups {
|
||||
object.insert(
|
||||
"groups".into(),
|
||||
build_groups(conn, section, view_mode, &tags)?,
|
||||
build_groups(conn, section, &tags)?,
|
||||
);
|
||||
let direct = load_node_entries(conn, §ion.key)?;
|
||||
let items = direct
|
||||
.iter()
|
||||
.map(|entry| item_for(entry, view_mode, &tags))
|
||||
.map(|entry| item_for(entry, &tags))
|
||||
.collect::<Vec<_>>();
|
||||
object.insert("items".into(), json!(items));
|
||||
} else {
|
||||
@@ -352,7 +353,7 @@ fn build_section(
|
||||
let entries = load_section_entries(conn, §ion.key)?;
|
||||
let items = entries
|
||||
.iter()
|
||||
.map(|entry| item_for(entry, view_mode, &tags))
|
||||
.map(|entry| item_for(entry, &tags))
|
||||
.collect::<Vec<_>>();
|
||||
object.insert("items".into(), json!(items));
|
||||
}
|
||||
@@ -360,20 +361,23 @@ fn build_section(
|
||||
Ok(Value::Object(object))
|
||||
}
|
||||
|
||||
/// 按条目类型组装列表项。
|
||||
fn item_for(entry: &EntryRow, view_mode: &str, tags: &HashMap<String, Vec<String>>) -> Value {
|
||||
match view_mode {
|
||||
/// 按条目自身的格式组装列表项。
|
||||
fn item_for(entry: &EntryRow, tags: &HashMap<String, Vec<String>>) -> Value {
|
||||
let mut value = match entry.format.as_str() {
|
||||
"posters" => poster_item(entry, tags.get(&entry.key)),
|
||||
"articles" => article_item(entry, tags.get(&entry.key)),
|
||||
_ => app_item(entry),
|
||||
};
|
||||
if let Some(object) = value.as_object_mut() {
|
||||
object.insert("format".into(), json!(entry.format));
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
/// 取出某个分区下的分组,并把条目按 `node_key` 分派进去。
|
||||
fn build_groups(
|
||||
conn: &Connection,
|
||||
section: &NodeRow,
|
||||
view_mode: &str,
|
||||
tags: &HashMap<String, Vec<String>>,
|
||||
) -> Result<Value, StashError> {
|
||||
let mut stmt = conn
|
||||
@@ -403,7 +407,7 @@ fn build_groups(
|
||||
let entries = load_node_entries(conn, &group_key)?;
|
||||
let items = entries
|
||||
.iter()
|
||||
.map(|entry| item_for(entry, view_mode, tags))
|
||||
.map(|entry| item_for(entry, tags))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
rendered_groups.push(json!({
|
||||
@@ -512,7 +516,8 @@ fn load_section_entries(
|
||||
let mut stmt = conn
|
||||
.prepare(
|
||||
"SELECT e.key, e.name, e.description, e.icon, e.href, e.cover,
|
||||
e.banner, e.badge, e.comment, e.summary, e.body, e.hot, e.views, e.likes
|
||||
e.banner, e.badge, e.comment, e.summary, e.body, e.hot, e.views, e.likes,
|
||||
COALESCE(e.format, 'apps')
|
||||
FROM entry e
|
||||
JOIN node p ON p.key = e.node_key
|
||||
WHERE COALESCE(p.parent_key, p.key) = ?1
|
||||
@@ -537,6 +542,7 @@ fn load_section_entries(
|
||||
hot: row.get(11)?,
|
||||
views: row.get(12)?,
|
||||
likes: row.get(13)?,
|
||||
format: row.get(14)?,
|
||||
})
|
||||
})
|
||||
.map_err(map_error)?
|
||||
@@ -549,7 +555,8 @@ fn load_section_entries(
|
||||
fn load_node_entries(conn: &Connection, node_key: &str) -> Result<Vec<EntryRow>, StashError> { let mut stmt = conn
|
||||
.prepare(
|
||||
"SELECT e.key, e.name, e.description, e.icon, e.href, e.cover,
|
||||
e.banner, e.badge, e.comment, e.summary, e.body, e.hot, e.views, e.likes
|
||||
e.banner, e.badge, e.comment, e.summary, e.body, e.hot, e.views, e.likes,
|
||||
COALESCE(e.format, 'apps')
|
||||
FROM entry e
|
||||
WHERE e.node_key = ?1
|
||||
ORDER BY e.sort, e.key",
|
||||
@@ -573,6 +580,7 @@ fn load_node_entries(conn: &Connection, node_key: &str) -> Result<Vec<EntryRow>,
|
||||
hot: row.get(11)?,
|
||||
views: row.get(12)?,
|
||||
likes: row.get(13)?,
|
||||
format: row.get(14)?,
|
||||
})
|
||||
})
|
||||
.map_err(map_error)?
|
||||
|
||||
Reference in New Issue
Block a user