Browse Source

Merge branch 'master' of http://47.98.153.252:3000/hejl/manta

# Conflicts:
#	win_text_editor/lib/app/providers/editor_provider.dart
#	win_text_editor/lib/app/widgets/file_explorer.dart
#	win_text_editor/lib/app/widgets/text_tab.dart
master
hejl 2 months ago
parent
commit
af6b4cd6ca
  1. 142
      win_text_editor/lib/app/providers/editor_provider.dart
  2. 59
      win_text_editor/lib/app/utils/file_utils.dart
  3. 9
      win_text_editor/lib/app/widgets/file_explorer.dart

142
win_text_editor/lib/app/providers/editor_provider.dart

@ -0,0 +1,142 @@ @@ -0,0 +1,142 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:win_text_editor/app/providers/logger.dart';
class EditorProvider with ChangeNotifier {
final List<EditorTab> _tabs = [];
String? _activeTabId;
bool _isLoading = false;
List<EditorTab> get tabs => _tabs;
String? get activeTabId => _activeTabId;
bool get isLoading => _isLoading;
int _templateTabCounter = 1;
final Map<String, bool> _tabLoadingStates = {};
final Map<String, int> _tabLoadedChunks = {};
bool isTabLoading(String tabId) => _tabLoadingStates[tabId] ?? false;
int getLoadedChunks(String tabId) => _tabLoadedChunks[tabId] ?? 0;
EditorTab? get activeTab {
if (_activeTabId == null) return null;
try {
return _tabs.firstWhere((tab) => tab.id == _activeTabId);
} catch (e) {
Logger().error("找不到活动选项卡: $_activeTabId", source: 'EditorProvider');
return null;
}
}
void addTab() {
final tabId = DateTime.now().millisecondsSinceEpoch.toString();
_tabs.add(
EditorTab(
id: tabId,
title: '模板解析[$_templateTabCounter]',
chunks: [], //
content: '',
),
);
_templateTabCounter++;
_activeTabId = tabId;
notifyListeners();
}
void closeTab(String tabId) {
Logger().info('关闭选项卡: $tabId');
_tabs.removeWhere((tab) => tab.id == tabId);
if (_activeTabId == tabId) {
_activeTabId = _tabs.isNotEmpty ? _tabs.last.id : null;
}
notifyListeners();
}
void setActiveTab(String tabId) {
Logger().info('设置活动选项卡: $tabId');
_activeTabId = tabId;
notifyListeners();
}
Future<void> updateContent(String tabId, String content, String? name) async {
_isLoading = true;
notifyListeners();
try {
final index = _tabs.indexWhere((t) => t.id == tabId);
if (index == -1) throw Exception("Tab not found");
_tabs[index] = EditorTab(
id: _tabs[index].id,
title: _tabs[index].title,
chunks: content.split('\n'), // chunks
fileName: name ?? _tabs[index].fileName,
content: content,
);
} finally {
_isLoading = false;
notifyListeners();
}
}
// EditorProvider
Future<void> updateContentInChunks(String tabId, String fullContent, String? fileName) async {
final lines = fullContent.split('\n');
const linesPerChunk = 1000; // 1000
_tabLoadingStates[tabId] = true;
notifyListeners();
try {
//
final index = _tabs.indexWhere((t) => t.id == tabId);
_tabs[index] = EditorTab(
id: tabId,
title: fileName ?? _tabs[index].title,
chunks: [],
fileName: fileName,
);
//
for (int i = 0; i < lines.length; i += linesPerChunk) {
if (!_tabLoadingStates[tabId]!) break; //
final chunkLines = lines.sublist(i, min(i + linesPerChunk, lines.length));
_tabs[index].chunks!.addAll(chunkLines);
notifyListeners();
await Future.delayed(Duration.zero); // UI更新
}
} finally {
_tabLoadingStates.remove(tabId);
notifyListeners();
}
}
void cancelLoading(String tabId) {
_tabLoadingStates[tabId] = false;
notifyListeners();
}
}
class EditorTab {
final String id;
String title;
List<String> chunks; //
String? fileName;
bool isHighlightEnabled;
String content;
EditorTab({
required this.id,
required this.title,
List<String>? chunks, //
this.fileName,
this.isHighlightEnabled = true,
this.content = '',
}) : chunks = chunks ?? []; //
String get fullContent => chunks.join('\n'); //
}

59
win_text_editor/lib/app/utils/file_utils.dart

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
// file_utils.dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'package:win_text_editor/app/providers/logger.dart';
class FileUtils {
static Future<String?> pickFile(BuildContext context) async {
try {
final result = await FilePicker.platform.pickFiles(type: FileType.any, allowMultiple: false);
return result?.files.single.path;
} catch (e) {
Logger().error('选择文件失败: ${e.toString()}');
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('选择文件失败: ${e.toString()}')));
}
return null;
}
}
static Future<String?> readFileContent(
BuildContext context,
String filePath, {
Duration timeout = const Duration(seconds: 30),
}) async {
try {
final content = await File(filePath).readAsString().timeout(
timeout,
onTimeout: () {
throw TimeoutException('文件加载超时,可能文件过大');
},
);
return content;
} on FormatException {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('这不是可读的文本文件')));
}
return null;
} on FileSystemException catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('文件访问错误: ${e.message}')));
}
return null;
} catch (e) {
Logger().error('读取文件失败: ${e.toString()}');
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('读取失败: ${e.toString()}')));
}
return null;
}
}
// showLoadingDialog方法
}

9
win_text_editor/lib/app/widgets/file_explorer.dart

@ -1,11 +1,13 @@ @@ -1,11 +1,13 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/providers/tab_provider.dart';
import 'package:win_text_editor/app/providers/logger.dart';
import '../models/file_node.dart';
import '../providers/file_provider.dart';
import '../providers/editor_provider.dart';
import 'dart:math';
@ -55,8 +57,11 @@ class _FileExplorerState extends State<FileExplorer> { @@ -55,8 +57,11 @@ class _FileExplorerState extends State<FileExplorer> {
final fileProvider = Provider.of<FileProvider>(context, listen: false);
if (node.isDirectory) {
await fileProvider.loadDirectoryContents(node);
} else {
// Handle file opening
print("No active tab found");
_openFileInEditor(context, node);
}
//
}
Future<void> _openFileInEditor(BuildContext context, FileNode node) async {

Loading…
Cancel
Save