mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: article content
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
|
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
|
||||||
import 'package:PiliPalaX/models/dynamics/article_content_model.dart';
|
import 'package:PiliPalaX/models/dynamics/article_content_model.dart';
|
||||||
import 'package:PiliPalaX/pages/preview/view.dart';
|
import 'package:PiliPalaX/pages/preview/view.dart';
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
|
|
||||||
class ArticleContent extends StatelessWidget {
|
class ArticleContent extends StatelessWidget {
|
||||||
const ArticleContent({
|
const ArticleContent({
|
||||||
@@ -15,22 +15,51 @@ class ArticleContent extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
List<String>? imgList = list
|
||||||
|
.where((item) => item.pic != null)
|
||||||
|
.toList()
|
||||||
|
.map((item) => item.pic?.pics?.first.url ?? '')
|
||||||
|
.toList();
|
||||||
return SliverList.separated(
|
return SliverList.separated(
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
itemBuilder: (_, index) {
|
itemBuilder: (_, index) {
|
||||||
ArticleContentModel item = list[index];
|
ArticleContentModel item = list[index];
|
||||||
if (item.insert is String) {
|
if (item.text != null) {
|
||||||
return SelectableText(
|
List<InlineSpan> spanList = [];
|
||||||
(item.insert as String).replaceAll('\n', '\n\n'),
|
item.text?.nodes?.forEach((item) {
|
||||||
style: TextStyle(
|
spanList.add(TextSpan(
|
||||||
letterSpacing: 0.3,
|
text: item.word?.words,
|
||||||
fontSize: FontSize.large.value,
|
style: TextStyle(
|
||||||
height: LineHeight.percent(125).size,
|
letterSpacing: 0.3,
|
||||||
fontWeight:
|
fontSize: FontSize.large.value,
|
||||||
item.attributes?.bold == true ? FontWeight.bold : null,
|
height: LineHeight.percent(125).size,
|
||||||
|
fontStyle:
|
||||||
|
item.word?.style?.italic == true ? FontStyle.italic : null,
|
||||||
|
color: item.word?.color != null
|
||||||
|
? Color(int.parse(
|
||||||
|
item.word!.color!.replaceFirst('#', 'FF'),
|
||||||
|
radix: 16,
|
||||||
|
))
|
||||||
|
: null,
|
||||||
|
decoration: item.word?.style?.strikethrough == true
|
||||||
|
? TextDecoration.lineThrough
|
||||||
|
: null,
|
||||||
|
fontWeight:
|
||||||
|
item.word?.style?.bold == true ? FontWeight.bold : null,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
});
|
||||||
|
return SelectableText.rich(TextSpan(children: spanList));
|
||||||
|
} else if (item.line != null) {
|
||||||
|
return Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
|
child: CachedNetworkImage(
|
||||||
|
imageUrl: item.line?.pic?.url ?? '',
|
||||||
|
height: item.line?.pic?.height?.toDouble(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (item.attributes?.clazz == 'normal-img') {
|
} else if (item.pic != null) {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (_, constraints) => GestureDetector(
|
builder: (_, constraints) => GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -39,8 +68,8 @@ class ArticleContent extends StatelessWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return ImagePreview(
|
return ImagePreview(
|
||||||
initialPage: 0,
|
initialPage: imgList.indexOf(item.pic!.pics!.first.url!),
|
||||||
imgList: [item.insert.nativeImage?.url],
|
imgList: imgList,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -48,22 +77,12 @@ class ArticleContent extends StatelessWidget {
|
|||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
width: constraints.maxWidth,
|
width: constraints.maxWidth,
|
||||||
height: constraints.maxWidth *
|
height: constraints.maxWidth *
|
||||||
item.insert.nativeImage?.height /
|
item.pic!.pics!.first.height! /
|
||||||
item.insert.nativeImage?.width,
|
item.pic!.pics!.first.width!,
|
||||||
src: item.insert.nativeImage?.url,
|
src: item.pic!.pics!.first.url,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
// return image(
|
|
||||||
// constrainedWidth,
|
|
||||||
// [
|
|
||||||
// ImageModel(
|
|
||||||
// width: item.insert.nativeImage?.width,
|
|
||||||
// height: item.insert.nativeImage?.height,
|
|
||||||
// url: item.insert.nativeImage?.url,
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// );
|
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
// return Text('unsupported content');
|
// return Text('unsupported content');
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:PiliPalaX/common/widgets/article_content.dart';
|
|
||||||
import 'package:PiliPalaX/models/dynamics/article_content_model.dart';
|
import 'package:PiliPalaX/models/dynamics/article_content_model.dart';
|
||||||
import 'package:html/dom.dart';
|
import 'package:html/dom.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
@@ -116,9 +115,11 @@ class HtmlHttp {
|
|||||||
final jsonString = match.group(1);
|
final jsonString = match.group(1);
|
||||||
if (jsonString != null) {
|
if (jsonString != null) {
|
||||||
try {
|
try {
|
||||||
opusContent = jsonDecode(jsonString)['readInfo']['content'];
|
dynamic json = jsonDecode(jsonString);
|
||||||
|
opusContent = json['readInfo']['content'];
|
||||||
try {
|
try {
|
||||||
opusContent = (jsonDecode(opusContent)['ops'] as List)
|
opusContent = (json['readInfo']?['opus']?['content']
|
||||||
|
?['paragraphs'] as List)
|
||||||
.map((item) => ArticleContentModel.fromJson(item))
|
.map((item) => ArticleContentModel.fromJson(item))
|
||||||
.toList();
|
.toList();
|
||||||
isJsonContent = true;
|
isJsonContent = true;
|
||||||
|
|||||||
@@ -1,72 +1,218 @@
|
|||||||
class ArticleContentModel {
|
class ArticleContentModel {
|
||||||
ArticleContentModel({
|
ArticleContentModel({
|
||||||
this.attributes,
|
this.paraType,
|
||||||
this.insert,
|
this.text,
|
||||||
|
this.format,
|
||||||
|
this.line,
|
||||||
});
|
});
|
||||||
Attributes? attributes;
|
int? paraType;
|
||||||
dynamic insert;
|
Text? text;
|
||||||
|
Format? format;
|
||||||
|
Line? line;
|
||||||
|
Pic? pic;
|
||||||
|
|
||||||
ArticleContentModel.fromJson(Map<String, dynamic> json) {
|
ArticleContentModel.fromJson(Map<String, dynamic> json) {
|
||||||
attributes = json['attributes'] == null
|
paraType = json['para_type'];
|
||||||
? null
|
text = json['text'] == null ? null : Text.fromJson(json['text']);
|
||||||
: Attributes.fromJson(json['attributes']);
|
format = json['format'] == null ? null : Format.fromJson(json['format']);
|
||||||
insert = json['insert'] == null
|
line = json['line'] == null ? null : Line.fromJson(json['line']);
|
||||||
? null
|
pic = json['pic'] == null ? null : Pic.fromJson(json['pic']);
|
||||||
: json['attributes']?['class'] == 'normal-img'
|
|
||||||
? Insert.fromJson(json['insert'])
|
|
||||||
: json['insert'];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Insert {
|
class Pic {
|
||||||
Insert({
|
Pic({
|
||||||
this.nativeImage,
|
|
||||||
});
|
|
||||||
NativeImage? nativeImage;
|
|
||||||
|
|
||||||
Insert.fromJson(Map<String, dynamic> json) {
|
|
||||||
nativeImage = json['native-image'] == null
|
|
||||||
? null
|
|
||||||
: NativeImage.fromJson(json['native-image']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NativeImage {
|
|
||||||
NativeImage({
|
|
||||||
this.alt,
|
|
||||||
this.url,
|
this.url,
|
||||||
this.width,
|
this.width,
|
||||||
this.height,
|
this.height,
|
||||||
this.size,
|
this.size,
|
||||||
this.status,
|
this.pics,
|
||||||
|
this.style,
|
||||||
});
|
});
|
||||||
|
String? url;
|
||||||
|
int? width;
|
||||||
|
int? height;
|
||||||
|
double? size;
|
||||||
|
List<Pic>? pics;
|
||||||
|
int? style;
|
||||||
|
|
||||||
dynamic alt;
|
Pic.fromJson(Map<String, dynamic> json) {
|
||||||
dynamic url;
|
|
||||||
dynamic width;
|
|
||||||
dynamic height;
|
|
||||||
dynamic size;
|
|
||||||
dynamic status;
|
|
||||||
|
|
||||||
NativeImage.fromJson(Map<String, dynamic> json) {
|
|
||||||
alt = json['alt'];
|
|
||||||
url = json['url'];
|
url = json['url'];
|
||||||
width = json['width'];
|
width = json['width'];
|
||||||
height = json['height'];
|
height = json['height'];
|
||||||
size = json['size'];
|
size = json['size'];
|
||||||
status = json['status'];
|
pics = (json['pics'] as List<dynamic>?)
|
||||||
|
?.map((item) => Pic.fromJson(item))
|
||||||
|
.toList();
|
||||||
|
style = json['style'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Attributes {
|
class Line {
|
||||||
Attributes({
|
Line({
|
||||||
this.clazz,
|
this.pic,
|
||||||
});
|
});
|
||||||
String? clazz;
|
Pic? pic;
|
||||||
bool? bold;
|
|
||||||
|
|
||||||
Attributes.fromJson(Map<String, dynamic> json) {
|
Line.fromJson(Map<String, dynamic> json) {
|
||||||
clazz = json['class'];
|
pic = json['pic'] == null ? null : Pic.fromJson(json['pic']);
|
||||||
bold = json['bold'];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Format {
|
||||||
|
Format({
|
||||||
|
this.align,
|
||||||
|
});
|
||||||
|
int? align;
|
||||||
|
|
||||||
|
Format.fromJson(Map<String, dynamic> json) {
|
||||||
|
align = json['align'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Text {
|
||||||
|
Text({
|
||||||
|
this.nodes,
|
||||||
|
});
|
||||||
|
List<Nodes>? nodes;
|
||||||
|
|
||||||
|
Text.fromJson(Map<String, dynamic> json) {
|
||||||
|
nodes = (json['nodes'] as List<dynamic>?)
|
||||||
|
?.map((item) => Nodes.fromJson(item))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Nodes {
|
||||||
|
Nodes({
|
||||||
|
this.nodeType,
|
||||||
|
this.word,
|
||||||
|
});
|
||||||
|
int? nodeType;
|
||||||
|
Word? word;
|
||||||
|
|
||||||
|
Nodes.fromJson(Map<String, dynamic> json) {
|
||||||
|
nodeType = json['node_type'];
|
||||||
|
word = json['word'] == null ? null : Word.fromJson(json['word']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Word {
|
||||||
|
Word({
|
||||||
|
this.words,
|
||||||
|
this.fontSize,
|
||||||
|
this.style,
|
||||||
|
this.color,
|
||||||
|
});
|
||||||
|
String? words;
|
||||||
|
int? fontSize;
|
||||||
|
Style? style;
|
||||||
|
String? color;
|
||||||
|
|
||||||
|
Word.fromJson(Map<String, dynamic> json) {
|
||||||
|
words = json['words'];
|
||||||
|
fontSize = json['font_size'];
|
||||||
|
style = json['style'] == null ? null : Style.fromJson(json['style']);
|
||||||
|
color = json['color'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Style {
|
||||||
|
Style({
|
||||||
|
this.bold,
|
||||||
|
this.italic,
|
||||||
|
this.strikethrough,
|
||||||
|
});
|
||||||
|
bool? bold;
|
||||||
|
bool? italic;
|
||||||
|
bool? strikethrough;
|
||||||
|
|
||||||
|
Style.fromJson(Map<String, dynamic> json) {
|
||||||
|
bold = json['bold'];
|
||||||
|
italic = json['italic'];
|
||||||
|
strikethrough = json['strikethrough'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// class ArticleContentModel {
|
||||||
|
// ArticleContentModel({
|
||||||
|
// this.attributes,
|
||||||
|
// this.insert,
|
||||||
|
// });
|
||||||
|
// Attributes? attributes;
|
||||||
|
// dynamic insert;
|
||||||
|
|
||||||
|
// ArticleContentModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
// attributes = json['attributes'] == null
|
||||||
|
// ? null
|
||||||
|
// : Attributes.fromJson(json['attributes']);
|
||||||
|
// insert = json['insert'] == null
|
||||||
|
// ? null
|
||||||
|
// : json['attributes']?['class'] == 'normal-img'
|
||||||
|
// ? Insert.fromJson(json['insert'])
|
||||||
|
// : json['insert'];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class Insert {
|
||||||
|
// Insert({
|
||||||
|
// this.nativeImage,
|
||||||
|
// });
|
||||||
|
// NativeImage? nativeImage;
|
||||||
|
|
||||||
|
// Insert.fromJson(Map<String, dynamic> json) {
|
||||||
|
// nativeImage = json['native-image'] == null
|
||||||
|
// ? null
|
||||||
|
// : NativeImage.fromJson(json['native-image']);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class NativeImage {
|
||||||
|
// NativeImage({
|
||||||
|
// this.alt,
|
||||||
|
// this.url,
|
||||||
|
// this.width,
|
||||||
|
// this.height,
|
||||||
|
// this.size,
|
||||||
|
// this.status,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// dynamic alt;
|
||||||
|
// dynamic url;
|
||||||
|
// dynamic width;
|
||||||
|
// dynamic height;
|
||||||
|
// dynamic size;
|
||||||
|
// dynamic status;
|
||||||
|
|
||||||
|
// NativeImage.fromJson(Map<String, dynamic> json) {
|
||||||
|
// alt = json['alt'];
|
||||||
|
// url = json['url'];
|
||||||
|
// width = json['width'];
|
||||||
|
// height = json['height'];
|
||||||
|
// size = json['size'];
|
||||||
|
// status = json['status'];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class Attributes {
|
||||||
|
// Attributes({
|
||||||
|
// this.clazz,
|
||||||
|
// this.bold,
|
||||||
|
// this.color,
|
||||||
|
// this.italic,
|
||||||
|
// this.strike,
|
||||||
|
// });
|
||||||
|
// String? clazz;
|
||||||
|
// bool? bold;
|
||||||
|
// String? color;
|
||||||
|
// bool? italic;
|
||||||
|
// bool? strike;
|
||||||
|
|
||||||
|
// Attributes.fromJson(Map<String, dynamic> json) {
|
||||||
|
// clazz = json['class'];
|
||||||
|
// bold = json['bold'];
|
||||||
|
// color = json['color'];
|
||||||
|
// italic = json['italic'];
|
||||||
|
// strike = json['strike'];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|||||||
Reference in New Issue
Block a user