提交 f5f637c7 authored 作者: yueweilu's avatar yueweilu

1、离线阅读基本逻辑

2、解密文件 3、笔记详情展示
上级 d36ba6ef
...@@ -288,7 +288,7 @@ abstract class LibraryAPI { ...@@ -288,7 +288,7 @@ abstract class LibraryAPI {
'types':types, 'types':types,
'content':content, 'content':content,
'is_open':isOpen, 'is_open':isOpen,
'color':content, 'color':color,
'positioning':positioning, 'positioning':positioning,
'note_content':noteContent 'note_content':noteContent
......
...@@ -413,5 +413,23 @@ abstract class MineAPI { ...@@ -413,5 +413,23 @@ abstract class MineAPI {
return false; return false;
} }
/// 25、删除笔记、高亮、划线的内容
static Future<bool> delNotes({
required String notesId,
required String bookId,
}) async {
final result = await HttpService.to.post(
'/v1/book/Information/delNotes',
params: {
'notes_id': notesId,
'book_id':bookId
},
);
if (result.data is Map && result.data['is_success'] == 1) {
return true;
}
return false;
}
} }
...@@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; ...@@ -7,6 +7,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_book/routes/index.dart'; import 'package:flutter_book/routes/index.dart';
import 'package:flutter_book/store/index.dart'; import 'package:flutter_book/store/index.dart';
import 'package:flutter_book/theme.dart'; import 'package:flutter_book/theme.dart';
import 'package:flutter_book/utils/index.dart';
import 'package:flutter_book/widgets/index.dart'; import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
...@@ -21,6 +22,10 @@ void main() { ...@@ -21,6 +22,10 @@ void main() {
Future.wait([ Future.wait([
UserStore.to.profile(), UserStore.to.profile(),
]).whenComplete(() { ]).whenComplete(() {
String name = EncryptUtil.aesEncrypt('我是谁');
print('2222222---------$name');
final result = EncryptUtil.aesDecrypt(name);
Console.log('解密--------------------------$result');
runApp(const MyApp()); runApp(const MyApp());
//FlutterNativeSplash.remove(); //FlutterNativeSplash.remove();
}); });
......
...@@ -2,7 +2,7 @@ library models; ...@@ -2,7 +2,7 @@ library models;
import 'package:just_audio/just_audio.dart' as just_audio;
part 'response.dart'; part 'response.dart';
part 'course.dart'; part 'course.dart';
......
...@@ -338,6 +338,7 @@ class UserInfoModel { ...@@ -338,6 +338,7 @@ class UserInfoModel {
/// 笔记详情模型 /// 笔记详情模型
class NoteModel { class NoteModel {
NoteModel({ NoteModel({
this.notesId,
this.types, this.types,
this.chapterId, this.chapterId,
this.content, this.content,
...@@ -348,33 +349,38 @@ class NoteModel { ...@@ -348,33 +349,38 @@ class NoteModel {
}); });
NoteModel.fromJson(dynamic json) { NoteModel.fromJson(dynamic json) {
types = json['notes_id'];
types = json['types']; types = json['types'];
chapterId = json['chapter_id']; chapterId = json['chapter_id'];
content = json['content']; content = json['content'];
positioning = json['positioning']; positioning = json['positioning'];
noteContent = json['note_content']; // noteContent = json['note_content'];
noteContent = json['note_content'] != null ? NoteContentModel.fromJson(json['note_content']) : null;
color = json['color']; color = json['color'];
chapterName = json['chapter_name']; chapterName = json['chapter_name'];
} }
num? notesId;
num? types; num? types;
num? chapterId; num? chapterId;
String? content; String? content;
String? positioning; String? positioning;
String? noteContent; // String? noteContent;
NoteContentModel? noteContent;
String? color; String? color;
String? chapterName; String? chapterName;
NoteModel copyWith({ NoteModel copyWith({
num? notesId,
num? types, num? types,
num? chapterId, num? chapterId,
String? content, String? content,
String? positioning, String? positioning,
String? noteContent, NoteContentModel? noteContent,
String? color, String? color,
String? chapterName, String? chapterName,
}) => }) =>
NoteModel( NoteModel(
notesId: notesId ?? this.notesId,
types: types ?? this.types, types: types ?? this.types,
chapterId: chapterId ?? this.chapterId, chapterId: chapterId ?? this.chapterId,
content: content ?? this.content, content: content ?? this.content,
...@@ -386,17 +392,105 @@ class NoteModel { ...@@ -386,17 +392,105 @@ class NoteModel {
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final map = <String, dynamic>{}; final map = <String, dynamic>{};
map['notes_id'] = notesId;
map['types'] = types; map['types'] = types;
map['chapter_id'] = chapterId; map['chapter_id'] = chapterId;
map['content'] = content; map['content'] = content;
map['positioning'] = positioning; map['positioning'] = positioning;
map['note_content'] = noteContent; if (noteContent != null) {
map['note_content'] = noteContent?.toJson();
}
map['color'] = color; map['color'] = color;
map['chapter_name'] = chapterName; map['chapter_name'] = chapterName;
return map; return map;
} }
} }
class MediaModel {
MediaModel({
this.privacyStatus,
this.content,
this.id,
this.duration = '',
});
MediaModel.fromJson(dynamic json) {
privacyStatus = json['privacy_status'];
content = json['content'];
id = json['id'];
duration = '';
}
num? privacyStatus;
String? content;
num? id;
late String duration;
MediaModel copyWith({ num? privacyStatus,
String? content,
num? id,
}) => MediaModel( privacyStatus: privacyStatus ?? this.privacyStatus,
content: content ?? this.content,
id: id ?? this.id,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['privacy_status'] = privacyStatus;
map['content'] = content;
map['id'] = id;
return map;
}
}
class NoteContentModel {
NoteContentModel({
this.text,
this.audio,
this.image,});
NoteContentModel.fromJson(dynamic json) {
text = json['text'] != null ? MediaModel.fromJson(json['text']) : null;
if (json['audio'] != null) {
audio = [];
json['audio'].forEach((v) {
audio?.add(MediaModel.fromJson(v));
});
}
if (json['image'] != null) {
image = [];
json['image'].forEach((v) {
image?.add(MediaModel.fromJson(v));
});
}
}
MediaModel? text;
List<MediaModel>? audio;
List<MediaModel>? image;
NoteContentModel copyWith({ MediaModel? text,
List<MediaModel>? audio,
List<MediaModel>? image,
}) => NoteContentModel( text: text ?? this.text,
audio: audio ?? this.audio,
image: image ?? this.image,
);
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
if (text != null) {
map['text'] = text?.toJson();
}
if (audio != null) {
map['audio'] = audio?.map((v) => v.toJson()).toList();
}
if (image != null) {
map['image'] = image?.map((v) => v.toJson()).toList();
}
return map;
}
}
/// 讨论模型 /// 讨论模型
class DiscussModel { class DiscussModel {
DiscussModel({ DiscussModel({
...@@ -662,3 +756,4 @@ class CoinModel { ...@@ -662,3 +756,4 @@ class CoinModel {
} }
} }
...@@ -8,6 +8,8 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -8,6 +8,8 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
final BookDetailModel bookDetailModel; final BookDetailModel bookDetailModel;
ReadController({required this.bookId, required this.chapterId,required this.chapterName,required this.bookDetailModel}); ReadController({required this.bookId, required this.chapterId,required this.chapterName,required this.bookDetailModel});
late InAppWebViewController webViewController;
// 目录 // 目录
List <ChapterModel> chapters = []; List <ChapterModel> chapters = [];
List <ToolModel> tools = [ List <ToolModel> tools = [
...@@ -38,7 +40,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -38,7 +40,7 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// 讨论添加的图片path数组 // 讨论添加的图片path数组
List <String> discussInputImages= []; List <String> discussInputImages= [];
// 讨论添加的语音path数组 // 讨论添加的语音path数组
List <String> discussInputAudios= []; List <AudioModel> discussInputAudios= [];
// 笔记标题 // 笔记标题
String noteTitle = ''; String noteTitle = '';
...@@ -61,10 +63,26 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -61,10 +63,26 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
} }
@override @override
void onReady() { void onReady() async {
// 上报开始阅读时间 // 上报开始阅读时间
_addReadTime(type: 'open'); _addReadTime(type: 'open');
_getChapters(); _getChapters();
final netStatus = await _checkCurrentNetStatus();
// 判断是否有离线文件 如果有使用离线阅读
final exist = await _isExistFile(bookId);
// 没有网并且有离线文件 离线阅读
if (netStatus && exist){
// webViewController.loadUrl(urlRequest: Uri.parse(urlRequest))
}
String path = await _getDirectory();
String finalPath = '$path/175/333.html';
final content = await readHtmlFileContent(finalPath);
// EncryptUtil.aesDecrypt(content);
Console.log('原始内容-----------------$content');
Console.log('解密-----------------${EncryptUtil.aesDecrypt(content!)}');
super.onReady(); super.onReady();
} }
...@@ -133,14 +151,14 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -133,14 +151,14 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
codec: Codec.aacMP4, codec: Codec.aacMP4,
); );
_mRecorder?.setSubscriptionDuration(Duration(milliseconds: 100)); // _mRecorder?.setSubscriptionDuration(Duration(milliseconds: 100));
_mRecorder?.onProgress?.listen((e) // _mRecorder?.onProgress?.listen((e)
{ // {
var date = DateTime.fromMillisecondsSinceEpoch(e.duration.inMilliseconds,isUtc: true); // var date = DateTime.fromMillisecondsSinceEpoch(e.duration.inMilliseconds,isUtc: true);
var text = DateFormat('mm:ss:').format(date); // var text = DateFormat('mm:ss:').format(date);
print('--------------$text'); // print('--------------$text');
//
}); // });
update(); update();
} }
// 停止录音 // 停止录音
...@@ -150,9 +168,19 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -150,9 +168,19 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
final path = await _mRecorder.stopRecorder(); final path = await _mRecorder.stopRecorder();
var duration = await audioPlayer.setFilePath(path!); var duration = await audioPlayer.setFilePath(path!);
print('-----duration---------------------$duration------'); print('-----duration---------------------$duration------');
AudioModel audioModel = AudioModel(path: path,duration: Tools.formatDuration(duration!));
discussInputAudios.add(audioModel);
update(); update();
} }
//
String formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return '${duration.inHours}:$twoDigitMinutes:${twoDigitSeconds}';
}
// 语音文件名称 // 语音文件名称
String generateVoiceFileName(){ String generateVoiceFileName(){
...@@ -205,8 +233,12 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -205,8 +233,12 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
Future<String> upload({ Future<String> upload({
required String path required String path
}) async { }) async {
String result = await CommonAPI.upload(path:path,fileTypes: 'comment'); // String result = await CommonAPI.upload(path:path,fileTypes: 'comment');
return result; // return result;
OssTool tool = OssTool('zxts-comment-file');
final response = await tool.putObjectFile(path);
print('------response--------------------------${response.realUri}');
return response.realUri.toString();
} }
// 提交 // 提交
...@@ -223,8 +255,8 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -223,8 +255,8 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
} }
// 循环上传音频获取地址 // 循环上传音频获取地址
for(String path in discussInputAudios){ for(AudioModel model in discussInputAudios){
final url = await upload(path: path); final url = await upload(path: model.path);
audios.add(url); audios.add(url);
} }
...@@ -249,28 +281,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -249,28 +281,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// 添加笔记 // 添加笔记
Future<bool> addNote(Map<String,dynamic> contentMap) async { Future<bool> addNote(Map<String,dynamic> contentMap) async {
//
// // 音频链接数组
// List<String> audios = [];
// // 图片链接数组
// List<String> images = [];
//
// // 循环上传图片获取地址
// for(String path in discussInputImages){
// final url = await upload(path: path);
// images.add(url);
// }
//
// // 循环上传音频获取地址
// for(String path in discussInputAudios){
// final url = await upload(path: path);
// audios.add(url);
// }
// Map<String,dynamic> contentMap = {
// 'text':contentInput.text,
// 'audio':audios,
// 'image':images
// };
final result = await LibraryAPI.addNote( final result = await LibraryAPI.addNote(
bookId: bookId, bookId: bookId,
...@@ -280,6 +290,10 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -280,6 +290,10 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
positioning: notePosition, positioning: notePosition,
noteContent: jsonEncode(contentMap) noteContent: jsonEncode(contentMap)
); );
titleInput.text = '';
contentInput.text = '';
setShowChat(false);
return result; return result;
} }
...@@ -287,28 +301,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -287,28 +301,6 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
// 发表评论 // 发表评论
// {String commentId ='0',String quoteContent ='',String title = ''} // {String commentId ='0',String quoteContent ='',String title = ''}
Future<bool> addDiscuss(Map<String,dynamic> contentMap) async{ Future<bool> addDiscuss(Map<String,dynamic> contentMap) async{
// // 音频链接数组
// List<String> audios = [];
// // 图片链接数组
// List<String> images = [];
//
// // 循环上传图片获取地址
// for(String path in discussInputImages){
// final url = await upload(path: path);
// images.add(url);
// }
//
// // 循环上传音频获取地址
// for(String path in discussInputAudios){
// final url = await upload(path: path);
// audios.add(url);
// }
//
// Map<String,dynamic> contentMap = {
// 'text':contentInput.text,
// 'audio':audios,
// 'image':images
// };
final result = await LibraryAPI.addDiscuss( final result = await LibraryAPI.addDiscuss(
bookId: bookId, bookId: bookId,
...@@ -394,6 +386,12 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -394,6 +386,12 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
} }
/// 获取离线文件路径 /// 获取离线文件路径
void getBookDown() async{ void getBookDown() async{
final exit = await _isExistFile(bookId);
// 存在离线文件
if (exit){
}
else{
final result = await LibraryAPI.getbookDownloadParam(bookId: bookId); final result = await LibraryAPI.getbookDownloadParam(bookId: bookId);
Console.log('----------_getBookDown------------------${result.download}'); Console.log('----------_getBookDown------------------${result.download}');
...@@ -407,6 +405,10 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -407,6 +405,10 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
extractZipFileFromCache(result.download!); extractZipFileFromCache(result.download!);
} }
}
// 下载文件
Future<void> extractZipFileFromCache(String url) async { Future<void> extractZipFileFromCache(String url) async {
// 从缓存中获取 ZIP 文件 // 从缓存中获取 ZIP 文件
var file = await DefaultCacheManager().getSingleFile(url); var file = await DefaultCacheManager().getSingleFile(url);
...@@ -433,7 +435,61 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide ...@@ -433,7 +435,61 @@ class ReadController extends FullLifeCycleController with GetSingleTickerProvide
} }
} }
// 判断当前网络状态
Future<bool> _checkCurrentNetStatus() async {
final connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none){
return false;
}
return true;
// if (connectivityResult == ConnectivityResult.mobile) {
// // I am connected to a mobile network.
// } else if (connectivityResult == ConnectivityResult.wifi) {
// // I am connected to a wifi network.
// } else if (connectivityResult == ConnectivityResult.ethernet) {
// // I am connected to a ethernet network.
// } else if (connectivityResult == ConnectivityResult.vpn) {
// // I am connected to a vpn network.
// // Note for iOS and macOS:
// // There is no separate network interface type for [vpn].
// // It returns [other] on any device (also simulator)
// } else if (connectivityResult == ConnectivityResult.bluetooth) {
// // I am connected to a bluetooth.
// } else if (connectivityResult == ConnectivityResult.other) {
// // I am connected to a network which is not in the above mentioned networks.
// } else if (connectivityResult == ConnectivityResult.none) {
// // I am not connected to any network.
// }
}
// 判断是否存在离线文件
Future<bool> _isExistFile(String bookId) async {
String directoryPath = await _getDirectory();
Directory directory = Directory(directoryPath);
bool directoryExists = await directory.exists();
if (directoryExists) {
print('存在名为 "$bookId" 的文件夹');
return await Directory('${directory.path}/$bookId').exists();
}
print('不存在名为 "$bookId" 的文件夹');
return false;
}
// 读取html内容
Future<String?> readHtmlFileContent(String filePath) async {
try {
File htmlFile = File(filePath);
String fileContent = await htmlFile.readAsString();
return fileContent;
}
catch (e){
print('Error reading file: $e');
return '';
}
}
//获取存储目录 //获取存储目录
Future<String> _getDirectory() async { Future<String> _getDirectory() async {
...@@ -486,5 +542,7 @@ class ToolModel { ...@@ -486,5 +542,7 @@ class ToolModel {
} }
class AudioModel { class AudioModel {
AudioModel({required this.path, required this.duration});
String path;
String duration;
} }
...@@ -7,6 +7,7 @@ import 'dart:typed_data'; ...@@ -7,6 +7,7 @@ import 'dart:typed_data';
import 'package:archive/archive.dart'; import 'package:archive/archive.dart';
import 'package:audio_session/audio_session.dart'; import 'package:audio_session/audio_session.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:easy_refresh/easy_refresh.dart'; import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_book/theme.dart'; import 'package:flutter_book/theme.dart';
......
...@@ -18,7 +18,6 @@ class ReadPage extends StatefulWidget { ...@@ -18,7 +18,6 @@ class ReadPage extends StatefulWidget {
} }
class _ReadPageState extends State<ReadPage> { class _ReadPageState extends State<ReadPage> {
late InAppWebViewController _webViewController;
@override @override
void initState() { void initState() {
...@@ -72,7 +71,7 @@ class _ReadPageState extends State<ReadPage> { ...@@ -72,7 +71,7 @@ class _ReadPageState extends State<ReadPage> {
options: ContextMenuOptions(hideDefaultSystemContextMenuItems: true), options: ContextMenuOptions(hideDefaultSystemContextMenuItems: true),
), ),
onWebViewCreated: (InAppWebViewController controller){ onWebViewCreated: (InAppWebViewController controller){
_webViewController = controller; readController.webViewController = controller;
}, },
onLoadStop: (controller, url) { onLoadStop: (controller, url) {
// flutter 主动给 js 传参数 // flutter 主动给 js 传参数
...@@ -252,7 +251,7 @@ class _ReadPageState extends State<ReadPage> { ...@@ -252,7 +251,7 @@ class _ReadPageState extends State<ReadPage> {
// 取消选中 tool // 取消选中 tool
controller.chooseTool(model); controller.chooseTool(model);
// 选择了新的章节 刷新 webview // 选择了新的章节 刷新 webview
_webViewController.reload(); controller.webViewController.reload();
}, },
); );
} }
......
...@@ -145,7 +145,7 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> { ...@@ -145,7 +145,7 @@ class _ReadInputDiscussState extends State<ReadInputDiscuss> {
), ),
); );
}, },
itemCount: 0, itemCount: widget.controller.discussInputAudios.length,
), ),
), ),
) )
......
...@@ -40,8 +40,8 @@ class _AboutPageState extends State<AboutPage> { ...@@ -40,8 +40,8 @@ class _AboutPageState extends State<AboutPage> {
margin: EdgeInsets.only(top: 55.w), margin: EdgeInsets.only(top: 55.w),
height: 60.w, height: 60.w,
width: 60.w, width: 60.w,
color: Colors.cyan, // color: Colors.cyan,
child: const CustomImage.asset(url: 'assets/images/banner.png'), child: const CustomImage.asset(url: 'assets/images/icon.png'),
), ),
Gaps.vGaps15, Gaps.vGaps15,
Text('紫荆数智学堂',style: TextStyle(fontSize: 17.w,fontWeight: Fonts.medium,color: Colours.c3),), Text('紫荆数智学堂',style: TextStyle(fontSize: 17.w,fontWeight: Fonts.medium,color: Colours.c3),),
......
part of user_edit_note;
class UserEditNoteController extends GetxController {
// 录音开始
bool startRecording = false;
// 笔记是否公开
bool isPublic = false;
// 设置笔记是否公开
void setIsPublic(){
isPublic = !isPublic;
update();
}
Future<bool> submit() async {
return true;
}
}
\ No newline at end of file
library user_edit_note;
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../../theme.dart';
import '../../utils/index.dart';
import '../../widgets/index.dart';
part 'view.dart';
part 'controller.dart';
\ No newline at end of file
part of user_edit_note;
class UserEditNotePage extends StatefulWidget {
const UserEditNotePage({Key? key}) : super(key: key);
@override
State<UserEditNotePage> createState() => _UserEditNotePageState();
}
class _UserEditNotePageState extends State<UserEditNotePage> {
@override
Widget build(BuildContext context) {
return GetBuilder<UserEditNoteController>(
init: UserEditNoteController(),
builder:(controller)=> Scaffold(
appBar: AppBar(
title: const Text('笔记编辑'),
centerTitle: true,
),
body: Container(
color: Colors.white,
margin: EdgeInsets.symmetric(horizontal: 15.w),
child: Column(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(4.w),
child: Container(
color: Colours.cF8,
constraints: BoxConstraints(
minHeight: 100.w
),
child: Column(
children: [
TextField(
// focusNode: widget.controller.discussContentFocusNode,
// maxLines: null,
// autofocus: widget.controller.chatType ==0?false:true,
// controller: widget.controller.contentInput,
decoration: InputDecoration(
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
hintText: '请输入内容',
hintStyle:TextStyle(fontSize: 12.w,height: 1.5,color: Colours.c9,),
filled: true,
fillColor: Colours.cF8,
),
),
Column(
children: [
GridView.builder(
// padding: const EdgeInsets.only(left: 13,top: 10),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 6,
crossAxisSpacing: 2.w,
mainAxisSpacing: 2.w,
childAspectRatio: 1
),
itemBuilder: (BuildContext context, int index) {
return Stack(
children: [
Positioned(
left:0,
right: 0,
top:0,
bottom: 0,
child: Container()
),
Positioned(
right: 5.w,
top: 5.w,
child: GestureDetector(
onTap: (){
},
child: Image.asset('assets/images/del_close.png',width: 12.w,height: 12.w,)
)
)
],
);
},
itemCount: 3,
),
Container(
color: Colors.red,
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index){
return Container(
height: 20.w,
margin: EdgeInsets.only(right: 130.w),
child: Container(
margin: EdgeInsets.only(top: 5.w),
padding: EdgeInsets.only(right:20.w,left: 10.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.w),
color: Colours.cF9,
),
child: Row(
// mainAxisSize: MainAxisSize.min,
mainAxisAlignment:MainAxisAlignment.spaceBetween,
children: [
Image.asset('assets/images/audio.png'),
Text('0:00/1:52',style: TextStyle(fontSize: 10.w,height: 1.4,color: Colours.c9),)
],
),
),
);
},
itemCount: 3,
),
),
)
],
)
],
),
),
),
Container(
padding: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
GestureDetector(
// onTap: () async{
// final assets = await AssetsPicker.image(
// context: context,
// );
// widget.controller.addDiscussInputImages(assets!.path);
// },
child: Image.asset('assets/images/read_add_img.png')),
Gaps.hGaps10,
GestureDetector(
onTap: () async {
// if(widget.controller.startRecording){
// widget.controller.stopRecorder();
// }
// else {
// widget.controller.record();
//
// }
},
child: Image.asset(controller.startRecording?'assets/images/stop.png':'assets/images/read_add_audio.png')
),
GestureDetector(
onTap: (){
controller.setIsPublic();
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Gaps.hGaps10,
Image.asset(controller.isPublic?'assets/images/pay_check.png':'assets/images/pay_uncheck.png'),
SizedBox(width: 3.w,),
Text('公开',style: TextStyle(fontSize: 13.w,height: 1.3,color: Colours.c9),)
],
),
)
],
),
GestureDetector(
onTap: (){
controller.submit();
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.w),
color: AppTheme.primary,
gradient: LinearGradient(
colors: [const Color(0xFFD53676).withOpacity(0.9),AppTheme.primary] , // 不可点击时的颜色,透明度为0.7
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
padding: EdgeInsets.symmetric(horizontal:13.5.w,vertical: 4.w),
child: Text('发表',style: TextStyle(fontSize: 14.w,fontWeight: Fonts.medium,color: Colors.white),),
),
)
],
),
)
],
)
),
),
);
}
}
...@@ -28,6 +28,12 @@ class UserNotesDesController extends GetxController { ...@@ -28,6 +28,12 @@ class UserNotesDesController extends GetxController {
super.onClose(); super.onClose();
} }
void delNotes({required String notesId,required String bookId}) async {
final result = await MineAPI.delNotes(notesId: notesId, bookId: bookId);
if (result){
onRefresh();
}
}
/// 获取笔记列表 /// 获取笔记列表
Future<void> _getNotes([bool isRefresh = false]) async { Future<void> _getNotes([bool isRefresh = false]) async {
...@@ -39,6 +45,15 @@ class UserNotesDesController extends GetxController { ...@@ -39,6 +45,15 @@ class UserNotesDesController extends GetxController {
bookId: model.bookId.toString(), bookId: model.bookId.toString(),
types: tag types: tag
); );
for(NoteModel noteModel in result){
if(noteModel.noteContent!.audio!.isNotEmpty){
for(MediaModel mediaModel in noteModel.noteContent!.audio!){
Duration? duration = await just_audio.AudioPlayer().setUrl(mediaModel.content??'');
mediaModel.duration = Tools.formatDuration(duration!);
}
}
}
// 如果是刷新 清理数据 // 如果是刷新 清理数据
if (isRefresh) notes.clear(); if (isRefresh) notes.clear();
notes.addAll(result); notes.addAll(result);
...@@ -54,6 +69,7 @@ class UserNotesDesController extends GetxController { ...@@ -54,6 +69,7 @@ class UserNotesDesController extends GetxController {
refreshController.finishRefresh(IndicatorResult.success); refreshController.finishRefresh(IndicatorResult.success);
refreshController.resetFooter(); refreshController.resetFooter();
} catch (error) { } catch (error) {
Console.log('----error--------------------------------------$error--------------');
refreshController.finishRefresh(IndicatorResult.fail); refreshController.finishRefresh(IndicatorResult.fail);
} }
} }
......
...@@ -4,12 +4,16 @@ import 'package:easy_refresh/easy_refresh.dart'; ...@@ -4,12 +4,16 @@ import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_book/widgets/index.dart'; import 'package:flutter_book/widgets/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:go_router/go_router.dart';
import '../../apis/index.dart'; import '../../apis/index.dart';
import '../../models/index.dart'; import '../../models/index.dart';
import '../../routes/index.dart';
import '../../theme.dart'; import '../../theme.dart';
import '../../utils/index.dart'; import '../../utils/index.dart';
import 'package:just_audio/just_audio.dart' as just_audio;
part 'view.dart'; part 'view.dart';
......
...@@ -2,16 +2,17 @@ part of user_notes_des; ...@@ -2,16 +2,17 @@ part of user_notes_des;
class BuildHigh extends StatelessWidget { class BuildHigh extends StatelessWidget {
final NoteModel model; final NoteModel model;
final void Function()? onTapDel;
const BuildHigh({ const BuildHigh({
Key? key, Key? key,
required this.model required this.model,
this.onTapDel
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w), margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w),
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.w), borderRadius: BorderRadius.circular(4.w),
color: Colors.white, color: Colors.white,
...@@ -24,6 +25,24 @@ class BuildHigh extends StatelessWidget { ...@@ -24,6 +25,24 @@ class BuildHigh extends StatelessWidget {
), ),
], ],
), ),
child: Slidable(
endActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
// An action can be bigger than the others.
onPressed: (BuildContext context){
onTapDel;
},
backgroundColor: const Color(0xFFAE1414),
foregroundColor: Colors.white,
// icon: Icons.archive,
label: '删除',
),
],
),
child: Container(
padding: EdgeInsets.all(10.w),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
...@@ -42,6 +61,8 @@ class BuildHigh extends StatelessWidget { ...@@ -42,6 +61,8 @@ class BuildHigh extends StatelessWidget {
) )
], ],
), ),
),
),
); );
} }
} }
...@@ -2,16 +2,17 @@ part of user_notes_des; ...@@ -2,16 +2,17 @@ part of user_notes_des;
class BuildLine extends StatelessWidget { class BuildLine extends StatelessWidget {
final NoteModel model; final NoteModel model;
final void Function()? onTapDel;
const BuildLine({ const BuildLine({
Key? key, Key? key,
required this.model required this.model,
this.onTapDel
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w), margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w),
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.w), borderRadius: BorderRadius.circular(4.w),
color: Colors.white, color: Colors.white,
...@@ -24,6 +25,24 @@ class BuildLine extends StatelessWidget { ...@@ -24,6 +25,24 @@ class BuildLine extends StatelessWidget {
), ),
], ],
), ),
child: Slidable(
endActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
// An action can be bigger than the others.
onPressed: (BuildContext context){
onTapDel;
},
backgroundColor: const Color(0xFFAE1414),
foregroundColor: Colors.white,
// icon: Icons.archive,
label: '删除',
),
],
),
child: Container(
padding: EdgeInsets.all(10.w),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
...@@ -45,6 +64,8 @@ class BuildLine extends StatelessWidget { ...@@ -45,6 +64,8 @@ class BuildLine extends StatelessWidget {
) )
], ],
), ),
),
),
); );
} }
} }
...@@ -28,15 +28,32 @@ class _BuildListPageState extends State<BuildListPage> with AutomaticKeepAliveCl ...@@ -28,15 +28,32 @@ class _BuildListPageState extends State<BuildListPage> with AutomaticKeepAliveCl
NoteModel model = controller.notes[index]; NoteModel model = controller.notes[index];
// 划线 // 划线
if(model.types == 1){ if(model.types == 1){
return BuildLine(model: model,); return BuildLine(model: model,
onTapDel: (){
controller.delNotes(notesId: model.notesId.toString(), bookId:widget.model.bookId.toString());
},
);
} }
// 高亮 // 高亮
else if(model.types == 2){ else if(model.types == 2){
return BuildHigh(model: model,); return BuildHigh(model: model,
onTapDel: (){
controller.delNotes(notesId: model.notesId.toString(), bookId:widget.model.bookId.toString());
},
);
} }
// 笔记 // 笔记
else if(model.types == 3){ else if(model.types == 3){
return BuildNote(model: model,); return BuildNote(model: model,
onTapDel: (){
print('---------删除--------');
controller.delNotes(notesId: model.notesId.toString(), bookId:widget.model.bookId.toString());
},
onTapEdit: (){
print('---------编辑--------');
context.pushNamed(Routes.editNote);
},
);
} }
}, },
itemCount: controller.notes.length, itemCount: controller.notes.length,
......
...@@ -2,16 +2,20 @@ part of user_notes_des; ...@@ -2,16 +2,20 @@ part of user_notes_des;
class BuildNote extends StatelessWidget { class BuildNote extends StatelessWidget {
final NoteModel model; final NoteModel model;
final void Function()? onTapDel;
final void Function()? onTapEdit;
const BuildNote({ const BuildNote({
Key? key, Key? key,
required this.model required this.model,
this.onTapDel,
this.onTapEdit
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w), margin: EdgeInsets.only(left: 10.w,right: 10.w,top: 10.w),
padding: EdgeInsets.all(10.w), // padding: EdgeInsets.all(10.w),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.w), borderRadius: BorderRadius.circular(4.w),
color: Colors.white, color: Colors.white,
...@@ -24,10 +28,41 @@ class BuildNote extends StatelessWidget { ...@@ -24,10 +28,41 @@ class BuildNote extends StatelessWidget {
), ),
], ],
), ),
child: Slidable(
endActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
// An action can be bigger than the others.
onPressed: (BuildContext context){
print('---------编辑1--------');
if (onTapEdit !=null) onTapEdit!();
},
backgroundColor: const Color(0xFFEB914A),
foregroundColor: Colors.white,
// icon: Icons.archive,
label: '编辑',
),
SlidableAction(
// An action can be bigger than the others.
onPressed: (BuildContext context){
print('---------删除1--------');
if (onTapDel !=null) onTapDel!();
},
backgroundColor: const Color(0xFFAE1414),
foregroundColor: Colors.white,
// icon: Icons.archive,
label: '删除',
),
],
),
child: Container(
padding: EdgeInsets.all(10.w),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('看来谁都不记得自己为何出现在此',style: TextStyle( Text(model.noteContent?.text?.content??'',style: TextStyle(
fontSize: 14.w, fontSize: 14.w,
height: 1.5, height: 1.5,
// color: Colors.red, // color: Colors.red,
...@@ -52,6 +87,8 @@ class BuildNote extends StatelessWidget { ...@@ -52,6 +87,8 @@ class BuildNote extends StatelessWidget {
) )
], ],
), ),
),
),
); );
} }
...@@ -69,11 +106,11 @@ class BuildNote extends StatelessWidget { ...@@ -69,11 +106,11 @@ class BuildNote extends StatelessWidget {
), ),
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return Container( return Container(
color: Colors.red, // color: Colors.red,
child: Center(child: Text('图片')), child: CustomImage.network(url: model.noteContent!.image![index].content??'',fit: BoxFit.cover,),
); );
}, },
itemCount: 3, itemCount: model.noteContent?.image?.length,
); );
} }
...@@ -82,6 +119,7 @@ class BuildNote extends StatelessWidget { ...@@ -82,6 +119,7 @@ class BuildNote extends StatelessWidget {
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
MediaModel mediaModel = model.noteContent!.audio![index];
return Container( return Container(
height: 20.w, height: 20.w,
margin: EdgeInsets.only(right: 130.w), margin: EdgeInsets.only(right: 130.w),
...@@ -97,33 +135,13 @@ class BuildNote extends StatelessWidget { ...@@ -97,33 +135,13 @@ class BuildNote extends StatelessWidget {
mainAxisAlignment:MainAxisAlignment.spaceBetween, mainAxisAlignment:MainAxisAlignment.spaceBetween,
children: [ children: [
Image.asset('assets/images/audio.png'), Image.asset('assets/images/audio.png'),
Text('0:00/1:52',style: TextStyle(fontSize: 10.w,height: 1.4,color: Colours.c9),) Text('0:00/${mediaModel.duration}',style: TextStyle(fontSize: 10.w,height: 1.4,color: Colours.c9),)
], ],
), ),
), ),
); );
}, },
itemCount: 3, itemCount: model.noteContent?.audio?.length,
); );
} }
// Widget _buildAudioGridView(){
// return GridView.builder(
// physics: const NeverScrollableScrollPhysics(),
// shrinkWrap: true,
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 6,
// crossAxisSpacing: 2.w,
// mainAxisSpacing: 2.w,
// childAspectRatio: 1
// ),
// itemBuilder: (BuildContext context, int index) {
// return Container(
// color: Colors.red,
// child: Center(child: Text('音频')),
// );
// },
// itemCount: 3,
// );
// }
} }
...@@ -45,6 +45,7 @@ import 'package:go_router/go_router.dart'; ...@@ -45,6 +45,7 @@ import 'package:go_router/go_router.dart';
import '../models/index.dart'; import '../models/index.dart';
import '../pages/bai_ke/index.dart'; import '../pages/bai_ke/index.dart';
import '../pages/read_web/index.dart'; import '../pages/read_web/index.dart';
import '../pages/user_edit_note/index.dart';
import '../pages/user_order/index.dart'; import '../pages/user_order/index.dart';
import '../pages/user_order_evaluate/index.dart'; import '../pages/user_order_evaluate/index.dart';
import '../pages/pay_coupon/index.dart'; import '../pages/pay_coupon/index.dart';
......
...@@ -79,6 +79,8 @@ abstract class Routes { ...@@ -79,6 +79,8 @@ abstract class Routes {
static const note = 'note'; static const note = 'note';
// 笔记详情 // 笔记详情
static const noteDes = 'note_des'; static const noteDes = 'note_des';
// 编辑笔记
static const editNote = 'edit_note';
// 错题 // 错题
static const wrong = 'wrong'; static const wrong = 'wrong';
// 讨论 // 讨论
...@@ -552,6 +554,15 @@ abstract class Routes { ...@@ -552,6 +554,15 @@ abstract class Routes {
) )
) )
), ),
GoRoute(
path: '/$editNote',
name: editNote,
pageBuilder: (context, state) =>CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const UserEditNotePage()
)
),
] ]
); );
......
library utils; library utils;
import 'dart:convert';
import 'dart:convert';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:ui'; import 'dart:ui';
......
...@@ -28,6 +28,13 @@ abstract class Tools { ...@@ -28,6 +28,13 @@ abstract class Tools {
} }
return DateFormat(pattern).format(dateTime); return DateFormat(pattern).format(dateTime);
} }
static String formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "${duration.inHours}:$twoDigitMinutes:$twoDigitSeconds";
}
} }
......
...@@ -97,6 +97,22 @@ packages: ...@@ -97,6 +97,22 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.17.1" version: "1.17.1"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.2"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.4"
convert: convert:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -129,6 +145,14 @@ packages: ...@@ -129,6 +145,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.0.6" version: "1.0.6"
dbus:
dependency: transitive
description:
name: dbus
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.10"
decimal: decimal:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -549,6 +573,14 @@ packages: ...@@ -549,6 +573,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
nm:
dependency: transitive
description:
name: nm
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.0"
oktoast: oktoast:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -685,6 +717,14 @@ packages: ...@@ -685,6 +717,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.1.3" version: "0.1.3"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.4.0"
platform: platform:
dependency: transitive dependency: transitive
description: description:
...@@ -938,6 +978,14 @@ packages: ...@@ -938,6 +978,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
xml:
dependency: transitive
description:
name: xml
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.3.0"
sdks: sdks:
dart: ">=3.0.0 <4.0.0" dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.0" flutter: ">=3.10.0"
...@@ -111,6 +111,8 @@ dependencies: ...@@ -111,6 +111,8 @@ dependencies:
flutter_cache_manager: ^3.3.1 flutter_cache_manager: ^3.3.1
# 解压 # 解压
archive: ^3.1.2 archive: ^3.1.2
# 判断当前网络情况
connectivity_plus: ^5.0.2
dev_dependencies: dev_dependencies:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论