feat(content): 分组对所有内容类型生效,统一分区/分组/条目模型
- 分区=容器,分组=任意类型的可选子容器,条目可挂分区或分组(直属分区) - catalog 所有类型统一输出 groups + items,条目查询按节点取 - 详情/侧栏/首页卡片预览支持所有类型的分组;多组显示小标题 + 未分组 - 后台「分区」「侧栏导航」对所有类型可建组;「内容」弹窗分组可选直属分区,编辑可换组 - 修复首页卡片配置为「整个分区」时预览为空 - 测试 59 passed
This commit is contained in:
+21
-25
@@ -223,32 +223,29 @@ pub fn load(conn: &Connection, key: &str) -> Result<Option<EntryRecord>, StashEr
|
||||
Ok(Some(entry))
|
||||
}
|
||||
|
||||
/// 条目能挂到哪:非应用分区本身,或应用分区下的分组。
|
||||
/// 条目能挂到哪:分区本身,或分区下的分组(任意类型都支持分组)。
|
||||
fn validate_target(conn: &Connection, node_key: &str) -> Result<(), StashError> {
|
||||
let row: Option<(String, Option<String>, Option<String>)> = conn
|
||||
let row: Option<(String, Option<String>)> = conn
|
||||
.query_row(
|
||||
"SELECT n.kind, n.view_mode, p.view_mode
|
||||
"SELECT n.kind, p.kind
|
||||
FROM node n LEFT JOIN node p ON p.key = n.parent_key
|
||||
WHERE n.key = ?1",
|
||||
[node_key],
|
||||
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
|
||||
|row| Ok((row.get(0)?, row.get(1)?)),
|
||||
)
|
||||
.optional()
|
||||
.map_err(map_error)?;
|
||||
|
||||
let Some((kind, view_mode, parent_mode)) = row else {
|
||||
let Some((kind, parent_kind)) = row else {
|
||||
return Err(StashError::NotFound(format!("节点不存在: {node_key}")));
|
||||
};
|
||||
|
||||
match kind.as_str() {
|
||||
"group" if parent_mode.as_deref() == Some("apps") => Ok(()),
|
||||
"group" => Err(StashError::InvalidDirectory(
|
||||
"分组只能挂在应用类分区下".into(),
|
||||
match (kind.as_str(), parent_kind.as_deref()) {
|
||||
("section", _) => Ok(()),
|
||||
("group", Some("section")) => Ok(()),
|
||||
("group", _) => Err(StashError::InvalidDirectory(
|
||||
"分组的父节点必须是分区".into(),
|
||||
)),
|
||||
"section" if view_mode.as_deref() == Some("apps") => Err(StashError::InvalidDirectory(
|
||||
"应用类条目必须选择一个分组".into(),
|
||||
)),
|
||||
"section" => Ok(()),
|
||||
_ => Err(StashError::InvalidDirectory(
|
||||
"条目只能挂在分区或分组下".into(),
|
||||
)),
|
||||
@@ -634,18 +631,17 @@ mod tests {
|
||||
.expect("应能查");
|
||||
assert_eq!(node, "g1");
|
||||
|
||||
// 应用分区不能直接挂条目,必须先选分组
|
||||
assert!(
|
||||
create(
|
||||
&mut conn,
|
||||
NewEntry {
|
||||
key: "bad-site".into(),
|
||||
node_key: "apps1".into(),
|
||||
item: patch("错误"),
|
||||
}
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
// 任何类型的分区都允许条目直属分区
|
||||
let direct = create(
|
||||
&mut conn,
|
||||
NewEntry {
|
||||
key: "direct-site".into(),
|
||||
node_key: "apps1".into(),
|
||||
item: patch("直属条目"),
|
||||
},
|
||||
)
|
||||
.expect("应能创建");
|
||||
assert_eq!(direct.key, "direct-site");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user