mod: article: show list

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-04-27 11:20:34 +08:00
parent 3dad24e7b4
commit dc1cca0d4c
6 changed files with 209 additions and 71 deletions

View File

@@ -1,3 +1,7 @@
import 'package:PiliPlus/models/dynamics/opus_detail/module.dart';
import 'package:PiliPlus/models/dynamics/opus_detail/module_content.dart';
import 'package:PiliPlus/models/dynamics/opus_detail/paragraph.dart';
import 'author.dart'; import 'author.dart';
import 'category.dart'; import 'category.dart';
import 'media.dart'; import 'media.dart';
@@ -44,6 +48,7 @@ class ArticleData {
int? versionId; int? versionId;
String? dynIdStr; String? dynIdStr;
int? totalArtNum; int? totalArtNum;
List<OpusModule>? modules;
ArticleData({ ArticleData({
this.id, this.id,
@@ -85,61 +90,77 @@ class ArticleData {
this.versionId, this.versionId,
this.dynIdStr, this.dynIdStr,
this.totalArtNum, this.totalArtNum,
this.modules,
}); });
factory ArticleData.fromJson(Map<String, dynamic> json) => ArticleData( factory ArticleData.fromJson(Map<String, dynamic> json) {
id: json['id'] as int?, final data = ArticleData(
category: json['category'] == null id: json['id'] as int?,
? null category: json['category'] == null
: Category.fromJson(json['category'] as Map<String, dynamic>), ? null
categories: (json['categories'] as List<dynamic>?) : Category.fromJson(json['category'] as Map<String, dynamic>),
?.map((e) => Category.fromJson(e as Map<String, dynamic>)) categories: (json['categories'] as List<dynamic>?)
.toList(), ?.map((e) => Category.fromJson(e as Map<String, dynamic>))
title: json['title'] as String?, .toList(),
summary: json['summary'] as String?, title: json['title'] as String?,
bannerUrl: json['banner_url'] as String?, summary: json['summary'] as String?,
templateId: json['template_id'] as int?, bannerUrl: json['banner_url'] as String?,
state: json['state'] as int?, templateId: json['template_id'] as int?,
author: json['author'] == null state: json['state'] as int?,
? null author: json['author'] == null
: Author.fromJson(json['author'] as Map<String, dynamic>), ? null
reprint: json['reprint'] as int?, : Author.fromJson(json['author'] as Map<String, dynamic>),
imageUrls: json['image_urls'], reprint: json['reprint'] as int?,
publishTime: json['publish_time'] as int?, imageUrls: json['image_urls'],
ctime: json['ctime'] as int?, publishTime: json['publish_time'] as int?,
mtime: json['mtime'] as int?, ctime: json['ctime'] as int?,
stats: json['stats'] == null mtime: json['mtime'] as int?,
? null stats: json['stats'] == null
: Stats.fromJson(json['stats'] as Map<String, dynamic>), ? null
tags: (json['tags'] as List<dynamic>?) : Stats.fromJson(json['stats'] as Map<String, dynamic>),
?.map((e) => Tag.fromJson(e as Map<String, dynamic>)) tags: (json['tags'] as List<dynamic>?)
.toList(), ?.map((e) => Tag.fromJson(e as Map<String, dynamic>))
words: json['words'] as int?, .toList(),
originImageUrls: json['origin_image_urls'], words: json['words'] as int?,
list: json['list'] as dynamic, originImageUrls: json['origin_image_urls'],
isLike: json['is_like'] as bool?, list: json['list'] as dynamic,
media: json['media'] == null isLike: json['is_like'] as bool?,
? null media: json['media'] == null
: Media.fromJson(json['media'] as Map<String, dynamic>), ? null
applyTime: json['apply_time'] as String?, : Media.fromJson(json['media'] as Map<String, dynamic>),
checkTime: json['check_time'] as String?, applyTime: json['apply_time'] as String?,
original: json['original'] as int?, checkTime: json['check_time'] as String?,
actId: json['act_id'] as int?, original: json['original'] as int?,
dispute: json['dispute'] as dynamic, actId: json['act_id'] as int?,
authenMark: json['authenMark'] as dynamic, dispute: json['dispute'] as dynamic,
coverAvid: json['cover_avid'] as int?, authenMark: json['authenMark'] as dynamic,
topVideoInfo: json['top_video_info'] as dynamic, coverAvid: json['cover_avid'] as int?,
type: json['type'] as int?, topVideoInfo: json['top_video_info'] as dynamic,
checkState: json['check_state'] as int?, type: json['type'] as int?,
originTemplateId: json['origin_template_id'] as int?, checkState: json['check_state'] as int?,
privatePub: json['private_pub'] as int?, originTemplateId: json['origin_template_id'] as int?,
contentPicList: json['content_pic_list'] as dynamic, privatePub: json['private_pub'] as int?,
content: json['content'] as String?, contentPicList: json['content_pic_list'] as dynamic,
keywords: json['keywords'] as String?, content: json['content'] as String?,
versionId: json['version_id'] as int?, keywords: json['keywords'] as String?,
dynIdStr: json['dyn_id_str'] as String?, versionId: json['version_id'] as int?,
totalArtNum: json['total_art_num'] as int?, dynIdStr: json['dyn_id_str'] as String?,
); totalArtNum: json['total_art_num'] as int?,
);
if (data.type == 3 && json['opus'] != null) {
data.modules = [
OpusModule(
moduleType: 'MODULE_TYPE_CONTENT',
moduleContent: ModuleContent(
paragraphs: (json['opus']?['content']?['paragraphs'] as List?)
?.map((e) => Paragraph.fromJson(e))
.toList(),
),
)
];
}
return data;
}
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'id': id, 'id': id,

View File

@@ -1,3 +1,4 @@
import 'node.dart';
import 'pic.dart'; import 'pic.dart';
import 'text.dart'; import 'text.dart';
@@ -9,6 +10,7 @@ class Paragraph {
Line? line; Line? line;
LinkCard? linkCard; LinkCard? linkCard;
Code? code; Code? code;
L1st? list;
Paragraph({ Paragraph({
this.align, this.align,
@@ -18,6 +20,7 @@ class Paragraph {
this.line, this.line,
this.linkCard, this.linkCard,
this.code, this.code,
this.list,
}); });
factory Paragraph.fromJson(Map<String, dynamic> json) => Paragraph( factory Paragraph.fromJson(Map<String, dynamic> json) => Paragraph(
@@ -34,6 +37,7 @@ class Paragraph {
? null ? null
: LinkCard.fromJson(json['link_card']), : LinkCard.fromJson(json['link_card']),
code: json['code'] == null ? null : Code.fromJson(json['code']), code: json['code'] == null ? null : Code.fromJson(json['code']),
list: json['list'] == null ? null : L1st.fromJson(json['list']),
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
@@ -44,6 +48,28 @@ class Paragraph {
}; };
} }
class L1st {
List<Item>? items;
int? style;
L1st.fromJson(Map<String, dynamic> json) {
items = (json['items'] as List?)?.map((e) => Item.fromJson(e)).toList();
style = json['style'];
}
}
class Item {
int? level;
int? order;
List<Node>? nodes;
Item.fromJson(Map<String, dynamic> json) {
level = json['level'];
order = json['order'];
nodes = (json['nodes'] as List?)?.map((e) => Node.fromJson(e)).toList();
}
}
class Code { class Code {
String? content; String? content;
String? lang; String? lang;

View File

@@ -5,8 +5,15 @@ class Word {
double? fontSize; double? fontSize;
Style? style; Style? style;
String? words; String? words;
String? fontLevel;
Word({this.color, this.fontSize, this.style, this.words}); Word({
this.color,
this.fontSize,
this.style,
this.words,
this.fontLevel,
});
factory Word.fromJson(Map<String, dynamic> json) => Word( factory Word.fromJson(Map<String, dynamic> json) => Word(
color: json['color'] == null color: json['color'] == null
@@ -17,6 +24,7 @@ class Word {
? null ? null
: Style.fromJson(json['style'] as Map<String, dynamic>), : Style.fromJson(json['style'] as Map<String, dynamic>),
words: json['words'] as String?, words: json['words'] as String?,
fontLevel: json['font_level'] as String?,
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
@@ -24,5 +32,6 @@ class Word {
'font_size': fontSize, 'font_size': fontSize,
'style': style?.toJson(), 'style': style?.toJson(),
'words': words, 'words': words,
'font_level': fontLevel,
}; };
} }

View File

@@ -339,7 +339,7 @@ class _ArticlePageState extends State<ArticlePage>
() { () {
if (_articleCtr.isLoaded.value) { if (_articleCtr.isLoaded.value) {
if (_articleCtr.type == 'read') { if (_articleCtr.type == 'read') {
var res = parser.parse(_articleCtr.articleData.content); late final res = parser.parse(_articleCtr.articleData.content);
return SliverMainAxisGroup( return SliverMainAxisGroup(
slivers: [ slivers: [
if (_articleCtr.articleData.title != null) if (_articleCtr.articleData.title != null)
@@ -403,19 +403,26 @@ class _ArticlePageState extends State<ArticlePage>
), ),
), ),
), ),
SliverList.separated( _articleCtr.articleData.modules?.isNotEmpty == true
itemCount: res.body!.children.length, ? opusContent(
itemBuilder: (context, index) { context: context,
return htmlRender( modules: _articleCtr.articleData.modules,
context: context, callback: _getImageCallback,
element: res.body!.children[index], maxWidth: maxWidth,
maxWidth: maxWidth, )
callback: _getImageCallback, : SliverList.separated(
); itemCount: res.body!.children.length,
}, itemBuilder: (context, index) {
separatorBuilder: (context, index) => return htmlRender(
const SizedBox(height: 10), context: context,
), element: res.body!.children[index],
maxWidth: maxWidth,
callback: _getImageCallback,
);
},
separatorBuilder: (context, index) =>
const SizedBox(height: 10),
),
], ],
); );
} else { } else {

View File

@@ -10,6 +10,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:re_highlight/languages/all.dart'; import 'package:re_highlight/languages/all.dart';
import 'package:re_highlight/re_highlight.dart'; import 'package:re_highlight/re_highlight.dart';
import 'package:re_highlight/styles/all.dart'; import 'package:re_highlight/styles/all.dart';
@@ -95,7 +96,7 @@ Widget opusContent({
itemBuilder: (context, index) { itemBuilder: (context, index) {
final element = item.moduleContent!.paragraphs![index]; final element = item.moduleContent!.paragraphs![index];
if ((element.paraType == 1 || element.paraType == 4)) { if (element.paraType == 1 || element.paraType == 4) {
return SelectableText.rich( return SelectableText.rich(
textAlign: element.align == 1 ? TextAlign.center : null, textAlign: element.align == 1 ? TextAlign.center : null,
TextSpan( TextSpan(
@@ -117,7 +118,7 @@ Widget opusContent({
), ),
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () { ..onTap = () {
if (item.rich!.jumpUrl != null) { if (item.rich?.jumpUrl != null) {
PiliScheme.routePushFromUrl( PiliScheme.routePushFromUrl(
item.rich!.jumpUrl!); item.rich!.jumpUrl!);
} }
@@ -182,6 +183,53 @@ Widget opusContent({
); );
} }
if (element.paraType == 5) {
return SelectableText.rich(
TextSpan(
children:
element.list?.items?.asMap().entries.map((entry) {
return TextSpan(
children: [
WidgetSpan(
child: Icon(MdiIcons.circleMedium),
alignment: PlaceholderAlignment.middle,
),
...entry.value.nodes!.map((item) {
return TextSpan(
children: [
TextSpan(
text: item.word?.words,
style: TextStyle(
decoration:
item.word?.style?.strikethrough ==
true
? TextDecoration.lineThrough
: null,
fontStyle:
item.word?.style?.italic == true
? FontStyle.italic
: null,
fontWeight: item.word?.style?.bold == true
? FontWeight.bold
: null,
color: item.word?.color != null
? Color(item.word!.color!)
: null,
fontSize: item.word?.fontSize,
),
),
],
);
}),
if (entry.key < element.list!.items!.length - 1)
TextSpan(text: '\n'),
],
);
}).toList(),
),
);
}
if (element.paraType == 6) { if (element.paraType == 6) {
if (element.linkCard?.card?.ugc != null) { if (element.linkCard?.card?.ugc != null) {
return Material( return Material(
@@ -263,6 +311,33 @@ Widget opusContent({
); );
} }
if (element.text?.nodes?.isNotEmpty == true) {
return SelectableText.rich(
textAlign: element.align == 1 ? TextAlign.center : null,
TextSpan(
children: element.text!.nodes!.map<TextSpan>((item) {
return TextSpan(
text: item.word?.words,
style: TextStyle(
decoration: item.word?.style?.strikethrough == true
? TextDecoration.lineThrough
: null,
fontStyle: item.word?.style?.italic == true
? FontStyle.italic
: null,
fontWeight: item.word?.style?.bold == true
? FontWeight.bold
: null,
color: item.word?.color != null
? Color(item.word!.color!)
: null,
fontSize: item.word?.fontSize,
),
);
}).toList()),
);
}
return const SizedBox.shrink(); return const SizedBox.shrink();
}, },
separatorBuilder: (BuildContext context, int index) => separatorBuilder: (BuildContext context, int index) =>

View File

@@ -243,7 +243,7 @@ Widget _itemWidget(BuildContext context, dynamic item) {
const SizedBox(width: 6), const SizedBox(width: 6),
Image.asset( Image.asset(
'assets/images/lv/lv${item['author']['level']}.png', 'assets/images/lv/lv${item['author']['level']}.png',
height: 19, height: 11,
), ),
], ],
), ),