feat(admin): 跳转规范化、侧栏子分类与归类管理
- 条目保存时规范化 href(裸域名补 https,拒绝 javascript:/data: 等协议),详情按钮按解析结果置灰/跳转
- settings.sidebar 增加 hidden_groups / shown_groups / group_order,侧栏 Tab 支持分组显隐与排序
- 新增 PATCH /api/categories/{id} 与「归类」Tab:编辑类型/标题/图标/色条/条数;改类型自动迁移条目与首页卡片
- 测试 54 passed(href 规范化 3 例、分类更新 3 例)
This commit is contained in:
@@ -28,8 +28,7 @@ export type ItemDetailModel = {
|
||||
}
|
||||
|
||||
/** 把应用条目转成详情模型 */
|
||||
export function appDetailModel(item: AppItem): ItemDetailModel {
|
||||
return {
|
||||
export function appDetailModel(item: AppItem): ItemDetailModel { return {
|
||||
title: item.name,
|
||||
cover: item.cover,
|
||||
shape: 'square',
|
||||
@@ -73,3 +72,38 @@ export function posterToRelatedItem(item: PosterItem): RelatedItem {
|
||||
cover: item.cover,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把库里的 `href` 解析成真正可跳转的地址。
|
||||
*
|
||||
* 兼容历史数据里没写协议的裸域名(`www.zhihu.com` → `https://…`);
|
||||
* 空值、`#` 与不安全/看不懂的协议返回 `undefined`,由页面把按钮置灰。
|
||||
*/
|
||||
export function resolveHref(raw: string | undefined): string | undefined {
|
||||
const value = raw?.trim() ?? ''
|
||||
if (value === '' || value === '#') return undefined
|
||||
|
||||
// 站内路径;`//host` 是协议相对地址,按外链补 https
|
||||
if (value.startsWith('/') && !value.startsWith('//')) return value
|
||||
if (/^https?:\/\//i.test(value)) return value
|
||||
if (value.startsWith('//')) return `https:${value}`
|
||||
|
||||
// 其他协议(javascript: / data: / mailto: …)一律不当作跳转
|
||||
if (/^[a-z][a-z0-9+.-]*:/i.test(value)) return undefined
|
||||
|
||||
return looksLikeDomain(value) ? `https://${value}` : undefined
|
||||
}
|
||||
|
||||
function looksLikeDomain(value: string): boolean {
|
||||
const [hostPort = ''] = value.split('/')
|
||||
const [host = ''] = hostPort.split(':')
|
||||
if (!host.includes('.')) return false
|
||||
|
||||
const parts = host.split('.')
|
||||
const tld = parts.pop() ?? ''
|
||||
return (
|
||||
tld.length >= 2 &&
|
||||
/^[a-z]+$/i.test(tld) &&
|
||||
parts.every((part) => /^[a-z0-9-]+$/i.test(part))
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user