feat: opus专栏内容渲染
This commit is contained in:
@ -146,20 +146,29 @@ class DynamicsController extends GetxController {
|
||||
/// 专栏文章查看
|
||||
case 'DYNAMIC_TYPE_ARTICLE':
|
||||
String title = item.modules.moduleDynamic.major.opus.title;
|
||||
String url = item.modules.moduleDynamic.major.opus.jumpUrl;
|
||||
if (url.contains('opus') || url.contains('read')) {
|
||||
String jumpUrl = item.modules.moduleDynamic.major.opus.jumpUrl;
|
||||
String url =
|
||||
jumpUrl.startsWith('//') ? jumpUrl.split('//').last : jumpUrl;
|
||||
if (jumpUrl.contains('opus') || jumpUrl.contains('read')) {
|
||||
RegExp digitRegExp = RegExp(r'\d+');
|
||||
Iterable<Match> matches = digitRegExp.allMatches(url);
|
||||
Iterable<Match> matches = digitRegExp.allMatches(jumpUrl);
|
||||
String number = matches.first.group(0)!;
|
||||
if (url.contains('read')) {
|
||||
if (jumpUrl.contains('read')) {
|
||||
number = 'cv$number';
|
||||
Get.toNamed('/htmlRender', parameters: {
|
||||
'url': url,
|
||||
'title': title,
|
||||
'id': number,
|
||||
'dynamicType': url.split('/')[1]
|
||||
});
|
||||
} else {
|
||||
Get.toNamed('/opus', parameters: {
|
||||
'url': url,
|
||||
'title': title,
|
||||
'id': number,
|
||||
'dynamicType': url.split('/')[1]
|
||||
});
|
||||
}
|
||||
Get.toNamed('/htmlRender', parameters: {
|
||||
'url': url.startsWith('//') ? url.split('//').last : url,
|
||||
'title': title,
|
||||
'id': number,
|
||||
'dynamicType': url.split('//').last.split('/')[1]
|
||||
});
|
||||
} else {
|
||||
Get.toNamed(
|
||||
'/webview',
|
||||
|
28
lib/pages/opus/controller.dart
Normal file
28
lib/pages/opus/controller.dart
Normal file
@ -0,0 +1,28 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/read.dart';
|
||||
import 'package:pilipala/models/read/opus.dart';
|
||||
|
||||
class OpusController extends GetxController {
|
||||
late String url;
|
||||
late String title;
|
||||
late String id;
|
||||
late String dynamicType;
|
||||
Rx<OpusDataModel> opusData = OpusDataModel().obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
url = Get.parameters['url']!;
|
||||
title = Get.parameters['title']!;
|
||||
id = Get.parameters['id']!;
|
||||
dynamicType = Get.parameters['dynamicType']!;
|
||||
}
|
||||
|
||||
Future fetchOpusData() async {
|
||||
var res = await ReadHttp.parseArticleOpus(id: id);
|
||||
if (res['status']) {
|
||||
opusData.value = res['data'];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
4
lib/pages/opus/index.dart
Normal file
4
lib/pages/opus/index.dart
Normal file
@ -0,0 +1,4 @@
|
||||
library opus;
|
||||
|
||||
export 'controller.dart';
|
||||
export 'view.dart';
|
42
lib/pages/opus/text_helper.dart
Normal file
42
lib/pages/opus/text_helper.dart
Normal file
@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/models/read/opus.dart';
|
||||
|
||||
class TextHelper {
|
||||
static Alignment getAlignment(int? align) {
|
||||
switch (align) {
|
||||
case 1:
|
||||
return Alignment.center;
|
||||
case 0:
|
||||
return Alignment.centerLeft;
|
||||
case 2:
|
||||
return Alignment.centerRight;
|
||||
default:
|
||||
return Alignment.centerLeft;
|
||||
}
|
||||
}
|
||||
|
||||
static TextSpan buildTextSpan(
|
||||
ModuleParagraphTextNode node, int? align, BuildContext context) {
|
||||
switch (node.type) {
|
||||
case 'TEXT_NODE_TYPE_WORD':
|
||||
return TextSpan(
|
||||
text: node.word?.words ?? '',
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
node.word?.fontSize != null ? node.word!.fontSize! * 0.95 : 14,
|
||||
fontWeight: node.word?.style?.bold != null
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
height: align == 1 ? 2 : 1.5,
|
||||
color: node.word?.color != null
|
||||
? Color(
|
||||
int.parse(node.word!.color!.substring(1, 7), radix: 16) +
|
||||
0xFF000000)
|
||||
: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
);
|
||||
default:
|
||||
return const TextSpan(text: '');
|
||||
}
|
||||
}
|
||||
}
|
230
lib/pages/opus/view.dart
Normal file
230
lib/pages/opus/view.dart
Normal file
@ -0,0 +1,230 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/models/read/opus.dart';
|
||||
import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart';
|
||||
import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart';
|
||||
import 'controller.dart';
|
||||
import 'text_helper.dart';
|
||||
|
||||
class OpusPage extends StatefulWidget {
|
||||
const OpusPage({super.key});
|
||||
|
||||
@override
|
||||
State<OpusPage> createState() => _OpusPageState();
|
||||
}
|
||||
|
||||
class _OpusPageState extends State<OpusPage> {
|
||||
final OpusController controller = Get.put(OpusController());
|
||||
late Future _futureBuilderFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_futureBuilderFuture = controller.fetchOpusData();
|
||||
}
|
||||
|
||||
void onPreviewImg(picList, initIndex, context) {
|
||||
Navigator.of(context).push(
|
||||
HeroDialogRoute<void>(
|
||||
builder: (BuildContext context) => InteractiveviewerGallery(
|
||||
sources: picList,
|
||||
initIndex: initIndex,
|
||||
itemBuilder: (
|
||||
BuildContext context,
|
||||
int index,
|
||||
bool isFocus,
|
||||
bool enablePageView,
|
||||
) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
if (enablePageView) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: Center(
|
||||
child: Hero(
|
||||
tag: picList[index],
|
||||
child: CachedNetworkImage(
|
||||
fadeInDuration: const Duration(milliseconds: 0),
|
||||
imageUrl: picList[index],
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onPageChanged: (int pageIndex) {},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_vert_rounded),
|
||||
onPressed: () {},
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 4),
|
||||
child: Text(
|
||||
controller.title,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 1,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
final modules = controller.opusData.value.detail!.modules!;
|
||||
final ModuleStat moduleStat = modules.last.moduleStat!;
|
||||
late ModuleContent moduleContent;
|
||||
final int moduleIndex = modules
|
||||
.indexWhere((module) => module.moduleContent != null);
|
||||
if (moduleIndex != -1) {
|
||||
moduleContent = modules[moduleIndex].moduleContent!;
|
||||
} else {
|
||||
print('No moduleContent found');
|
||||
}
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(16, 0, 16,
|
||||
MediaQuery.of(context).padding.bottom + 40),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 0, 0, 20),
|
||||
child: SelectableText.rich(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
fontSize: 12,
|
||||
letterSpacing: 1,
|
||||
),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${moduleStat.comment!.count}评论'),
|
||||
const TextSpan(text: ' '),
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(text: '${moduleStat.like!.count}赞'),
|
||||
const TextSpan(text: ' '),
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(
|
||||
text: '${moduleStat.favorite!.count}转发'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
...moduleContent.paragraphs!.map(
|
||||
(ModuleParagraph paragraph) {
|
||||
return Column(
|
||||
children: [
|
||||
if (paragraph.paraType == 1) ...[
|
||||
Container(
|
||||
alignment: TextHelper.getAlignment(
|
||||
paragraph.align),
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: paragraph.text?.nodes
|
||||
?.map((node) {
|
||||
return TextHelper.buildTextSpan(
|
||||
node,
|
||||
paragraph.align,
|
||||
context);
|
||||
}).toList() ??
|
||||
[],
|
||||
),
|
||||
),
|
||||
)
|
||||
] else if (paragraph.paraType == 2) ...[
|
||||
...paragraph.pic?.pics?.map(
|
||||
(Pic pic) => Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10, bottom: 10),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
onPreviewImg(
|
||||
paragraph.pic!.pics!
|
||||
.map((pic) => pic.url)
|
||||
.toList(),
|
||||
paragraph.pic!.pics!
|
||||
.indexWhere((pic) =>
|
||||
pic.url ==
|
||||
pic.url),
|
||||
context);
|
||||
},
|
||||
child: NetworkImgLayer(
|
||||
src: pic.url,
|
||||
width: (Get.size.width - 32) *
|
||||
pic.scale!,
|
||||
height:
|
||||
(Get.size.width - 32) *
|
||||
pic.scale! /
|
||||
pic.aspectRatio!,
|
||||
type: 'emote',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
) ??
|
||||
[],
|
||||
] else
|
||||
const SizedBox(),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 请求错误
|
||||
return SizedBox(
|
||||
height: 100,
|
||||
child: Center(
|
||||
child: Text(snapshot.data['message']),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return const SizedBox(
|
||||
height: 100,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user