From dc1cca0d4cda7e26ac6c0f2c9c5c14acbf8699a0 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Sun, 27 Apr 2025 11:20:34 +0800 Subject: [PATCH] mod: article: show list Signed-off-by: bggRGjQaUbCoE --- lib/models/dynamics/article_view/data.dart | 127 ++++++++++-------- .../dynamics/opus_detail/paragraph.dart | 26 ++++ lib/models/dynamics/opus_detail/word.dart | 11 +- lib/pages/article/view.dart | 35 +++-- lib/pages/article/widgets/opus_content.dart | 79 ++++++++++- .../video/detail/note/note_list_page.dart | 2 +- 6 files changed, 209 insertions(+), 71 deletions(-) diff --git a/lib/models/dynamics/article_view/data.dart b/lib/models/dynamics/article_view/data.dart index 8889da01..fda2a014 100644 --- a/lib/models/dynamics/article_view/data.dart +++ b/lib/models/dynamics/article_view/data.dart @@ -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 'category.dart'; import 'media.dart'; @@ -44,6 +48,7 @@ class ArticleData { int? versionId; String? dynIdStr; int? totalArtNum; + List? modules; ArticleData({ this.id, @@ -85,61 +90,77 @@ class ArticleData { this.versionId, this.dynIdStr, this.totalArtNum, + this.modules, }); - factory ArticleData.fromJson(Map json) => ArticleData( - id: json['id'] as int?, - category: json['category'] == null - ? null - : Category.fromJson(json['category'] as Map), - categories: (json['categories'] as List?) - ?.map((e) => Category.fromJson(e as Map)) - .toList(), - title: json['title'] as String?, - summary: json['summary'] as String?, - bannerUrl: json['banner_url'] as String?, - templateId: json['template_id'] as int?, - state: json['state'] as int?, - author: json['author'] == null - ? null - : Author.fromJson(json['author'] as Map), - reprint: json['reprint'] as int?, - imageUrls: json['image_urls'], - publishTime: json['publish_time'] as int?, - ctime: json['ctime'] as int?, - mtime: json['mtime'] as int?, - stats: json['stats'] == null - ? null - : Stats.fromJson(json['stats'] as Map), - tags: (json['tags'] as List?) - ?.map((e) => Tag.fromJson(e as Map)) - .toList(), - words: json['words'] as int?, - originImageUrls: json['origin_image_urls'], - list: json['list'] as dynamic, - isLike: json['is_like'] as bool?, - media: json['media'] == null - ? null - : Media.fromJson(json['media'] as Map), - applyTime: json['apply_time'] as String?, - checkTime: json['check_time'] as String?, - original: json['original'] as int?, - actId: json['act_id'] as int?, - dispute: json['dispute'] as dynamic, - authenMark: json['authenMark'] as dynamic, - coverAvid: json['cover_avid'] as int?, - topVideoInfo: json['top_video_info'] as dynamic, - type: json['type'] as int?, - checkState: json['check_state'] as int?, - originTemplateId: json['origin_template_id'] as int?, - privatePub: json['private_pub'] as int?, - contentPicList: json['content_pic_list'] as dynamic, - content: json['content'] as String?, - keywords: json['keywords'] as String?, - versionId: json['version_id'] as int?, - dynIdStr: json['dyn_id_str'] as String?, - totalArtNum: json['total_art_num'] as int?, - ); + factory ArticleData.fromJson(Map json) { + final data = ArticleData( + id: json['id'] as int?, + category: json['category'] == null + ? null + : Category.fromJson(json['category'] as Map), + categories: (json['categories'] as List?) + ?.map((e) => Category.fromJson(e as Map)) + .toList(), + title: json['title'] as String?, + summary: json['summary'] as String?, + bannerUrl: json['banner_url'] as String?, + templateId: json['template_id'] as int?, + state: json['state'] as int?, + author: json['author'] == null + ? null + : Author.fromJson(json['author'] as Map), + reprint: json['reprint'] as int?, + imageUrls: json['image_urls'], + publishTime: json['publish_time'] as int?, + ctime: json['ctime'] as int?, + mtime: json['mtime'] as int?, + stats: json['stats'] == null + ? null + : Stats.fromJson(json['stats'] as Map), + tags: (json['tags'] as List?) + ?.map((e) => Tag.fromJson(e as Map)) + .toList(), + words: json['words'] as int?, + originImageUrls: json['origin_image_urls'], + list: json['list'] as dynamic, + isLike: json['is_like'] as bool?, + media: json['media'] == null + ? null + : Media.fromJson(json['media'] as Map), + applyTime: json['apply_time'] as String?, + checkTime: json['check_time'] as String?, + original: json['original'] as int?, + actId: json['act_id'] as int?, + dispute: json['dispute'] as dynamic, + authenMark: json['authenMark'] as dynamic, + coverAvid: json['cover_avid'] as int?, + topVideoInfo: json['top_video_info'] as dynamic, + type: json['type'] as int?, + checkState: json['check_state'] as int?, + originTemplateId: json['origin_template_id'] as int?, + privatePub: json['private_pub'] as int?, + contentPicList: json['content_pic_list'] as dynamic, + content: json['content'] as String?, + keywords: json['keywords'] as String?, + versionId: json['version_id'] 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 toJson() => { 'id': id, diff --git a/lib/models/dynamics/opus_detail/paragraph.dart b/lib/models/dynamics/opus_detail/paragraph.dart index e69ce5e2..ad98ad63 100644 --- a/lib/models/dynamics/opus_detail/paragraph.dart +++ b/lib/models/dynamics/opus_detail/paragraph.dart @@ -1,3 +1,4 @@ +import 'node.dart'; import 'pic.dart'; import 'text.dart'; @@ -9,6 +10,7 @@ class Paragraph { Line? line; LinkCard? linkCard; Code? code; + L1st? list; Paragraph({ this.align, @@ -18,6 +20,7 @@ class Paragraph { this.line, this.linkCard, this.code, + this.list, }); factory Paragraph.fromJson(Map json) => Paragraph( @@ -34,6 +37,7 @@ class Paragraph { ? null : LinkCard.fromJson(json['link_card']), code: json['code'] == null ? null : Code.fromJson(json['code']), + list: json['list'] == null ? null : L1st.fromJson(json['list']), ); Map toJson() => { @@ -44,6 +48,28 @@ class Paragraph { }; } +class L1st { + List? items; + int? style; + + L1st.fromJson(Map json) { + items = (json['items'] as List?)?.map((e) => Item.fromJson(e)).toList(); + style = json['style']; + } +} + +class Item { + int? level; + int? order; + List? nodes; + + Item.fromJson(Map json) { + level = json['level']; + order = json['order']; + nodes = (json['nodes'] as List?)?.map((e) => Node.fromJson(e)).toList(); + } +} + class Code { String? content; String? lang; diff --git a/lib/models/dynamics/opus_detail/word.dart b/lib/models/dynamics/opus_detail/word.dart index 32778201..84d93abb 100644 --- a/lib/models/dynamics/opus_detail/word.dart +++ b/lib/models/dynamics/opus_detail/word.dart @@ -5,8 +5,15 @@ class Word { double? fontSize; Style? style; 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 json) => Word( color: json['color'] == null @@ -17,6 +24,7 @@ class Word { ? null : Style.fromJson(json['style'] as Map), words: json['words'] as String?, + fontLevel: json['font_level'] as String?, ); Map toJson() => { @@ -24,5 +32,6 @@ class Word { 'font_size': fontSize, 'style': style?.toJson(), 'words': words, + 'font_level': fontLevel, }; } diff --git a/lib/pages/article/view.dart b/lib/pages/article/view.dart index aa1f9d82..ed43a014 100644 --- a/lib/pages/article/view.dart +++ b/lib/pages/article/view.dart @@ -339,7 +339,7 @@ class _ArticlePageState extends State () { if (_articleCtr.isLoaded.value) { if (_articleCtr.type == 'read') { - var res = parser.parse(_articleCtr.articleData.content); + late final res = parser.parse(_articleCtr.articleData.content); return SliverMainAxisGroup( slivers: [ if (_articleCtr.articleData.title != null) @@ -403,19 +403,26 @@ class _ArticlePageState extends State ), ), ), - SliverList.separated( - itemCount: res.body!.children.length, - itemBuilder: (context, index) { - return htmlRender( - context: context, - element: res.body!.children[index], - maxWidth: maxWidth, - callback: _getImageCallback, - ); - }, - separatorBuilder: (context, index) => - const SizedBox(height: 10), - ), + _articleCtr.articleData.modules?.isNotEmpty == true + ? opusContent( + context: context, + modules: _articleCtr.articleData.modules, + callback: _getImageCallback, + maxWidth: maxWidth, + ) + : SliverList.separated( + itemCount: res.body!.children.length, + itemBuilder: (context, index) { + return htmlRender( + context: context, + element: res.body!.children[index], + maxWidth: maxWidth, + callback: _getImageCallback, + ); + }, + separatorBuilder: (context, index) => + const SizedBox(height: 10), + ), ], ); } else { diff --git a/lib/pages/article/widgets/opus_content.dart b/lib/pages/article/widgets/opus_content.dart index 1b4db190..46253db9 100644 --- a/lib/pages/article/widgets/opus_content.dart +++ b/lib/pages/article/widgets/opus_content.dart @@ -10,6 +10,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.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/re_highlight.dart'; import 'package:re_highlight/styles/all.dart'; @@ -95,7 +96,7 @@ Widget opusContent({ itemBuilder: (context, index) { final element = item.moduleContent!.paragraphs![index]; - if ((element.paraType == 1 || element.paraType == 4)) { + if (element.paraType == 1 || element.paraType == 4) { return SelectableText.rich( textAlign: element.align == 1 ? TextAlign.center : null, TextSpan( @@ -117,7 +118,7 @@ Widget opusContent({ ), recognizer: TapGestureRecognizer() ..onTap = () { - if (item.rich!.jumpUrl != null) { + if (item.rich?.jumpUrl != null) { PiliScheme.routePushFromUrl( 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.linkCard?.card?.ugc != null) { 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((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(); }, separatorBuilder: (BuildContext context, int index) => diff --git a/lib/pages/video/detail/note/note_list_page.dart b/lib/pages/video/detail/note/note_list_page.dart index b869944a..84da19cc 100644 --- a/lib/pages/video/detail/note/note_list_page.dart +++ b/lib/pages/video/detail/note/note_list_page.dart @@ -243,7 +243,7 @@ Widget _itemWidget(BuildContext context, dynamic item) { const SizedBox(width: 6), Image.asset( 'assets/images/lv/lv${item['author']['level']}.png', - height: 19, + height: 11, ), ], ),