import '../../../../core/utils/text.dart'; /// 一张站点卡片,对应 `index.html` 里的 `.card`。 class LinkItem { const LinkItem({ required this.id, required this.title, required this.url, this.categoryId = '', this.description = '', this.editable = false, }); /// 从 JSON 构造(将来接后端接口时使用)。 factory LinkItem.fromJson(Map json) { return LinkItem( id: json['id'] as String, title: json['title'] as String, url: json['url'] as String, categoryId: json['categoryId'] as String? ?? '', description: json['description'] as String? ?? '', editable: json['editable'] as bool? ?? false, ); } /// 唯一标识。 final String id; /// 站点名称,同时用于生成卡片左上角的首字母图标。 final String title; /// 跳转地址。 final String url; /// 所属分组 id。编辑时要靠它反查出分组名,才能把表单预填对。 final String categoryId; /// 一句话描述。 final String description; /// 这条能不能改。 /// /// 后端按 `source != "system"` 推导出 `editable`:系统内置的条目只读, /// 卡片上不会出现编辑/删除;用户提交和接口推送进来的可以改、可以删。 final bool editable; /// 卡片图标显示的首字符。 String get initial => firstChar(title).toUpperCase(); /// 展示用域名。 String get host => Uri.tryParse(url)?.host ?? url; /// 序列化为 JSON。 Map toJson() => { 'id': id, 'title': title, 'url': url, 'categoryId': categoryId, 'description': description, 'editable': editable, }; @override bool operator ==(Object other) { return identical(this, other) || other is LinkItem && other.id == id && other.title == title && other.url == url && other.categoryId == categoryId && other.description == description && other.editable == editable; } @override int get hashCode => Object.hash(id, title, url, categoryId, description, editable); @override String toString() => 'LinkItem(id: $id, title: $title)'; }