webdav存在自引用问题,现版本修复完成

This commit is contained in:
2026-08-17 22:24:56 +08:00
parent 457a9bf1e5
commit a6056c7f1a
5 changed files with 874 additions and 622 deletions
+537 -467
View File
File diff suppressed because it is too large Load Diff
+81 -100
View File
@@ -1,9 +1,4 @@
// ============================================================
// 文件名: webdav_file_list_page.dart
// 功能: WebDAV 文件浏览页面,支持文件夹导航和音乐播放
// 调用方式: Navigator.push(context, MaterialPageRoute(builder: (_) => WebDAVFileListPage()))
// ============================================================
// lib/pages/webdav_file_list_page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../services/webdav_service.dart';
@@ -11,7 +6,6 @@ import '../services/playback_service.dart';
import '../services/audio_service.dart';
class WebDAVFileListPage extends StatefulWidget {
/// 当前浏览路径,默认为根目录 '/'
final String currentPath;
const WebDAVFileListPage({super.key, this.currentPath = '/'});
@@ -26,6 +20,15 @@ class _WebDAVFileListPageState extends State<WebDAVFileListPage> {
String _errorMessage = '';
String _currentPath = '/';
// 安全解码
String _safeDecode(String input) {
try {
return Uri.decodeComponent(input);
} catch (_) {
return input; // 解码失败时返回原始字符串
}
}
@override
void initState() {
super.initState();
@@ -33,9 +36,6 @@ class _WebDAVFileListPageState extends State<WebDAVFileListPage> {
_loadDirectory();
}
// -------------------------------------------------------------
// 加载当前目录内容
// -------------------------------------------------------------
Future<void> _loadDirectory() async {
setState(() {
_isLoading = true;
@@ -57,9 +57,6 @@ class _WebDAVFileListPageState extends State<WebDAVFileListPage> {
}
}
// -------------------------------------------------------------
// 进入子目录
// -------------------------------------------------------------
void _enterDirectory(WebDAVItem dir) {
Navigator.push(
context,
@@ -69,9 +66,6 @@ class _WebDAVFileListPageState extends State<WebDAVFileListPage> {
);
}
// -------------------------------------------------------------
// 播放音乐文件
// -------------------------------------------------------------
void _playSong(WebDAVItem file) async {
try {
final url = WebDAVService.instance.getFileUrl(file.path);
@@ -79,13 +73,11 @@ class _WebDAVFileListPageState extends State<WebDAVFileListPage> {
final song = Song(
id: file.path,
title: file.name.replaceAll(RegExp(r'\.[^.]*$'), ''),
title: _safeDecode(file.name).replaceAll(RegExp(r'\.[^.]*$'), ''),
artist: '未知艺术家',
url: url,
);
context.read<AudioService>().playSong(song);
// ✅ 移除 SnackBar,直接显示 MiniPlayer
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -98,12 +90,13 @@ class _WebDAVFileListPageState extends State<WebDAVFileListPage> {
}
}
// -------------------------------------------------------------
// 构建面包屑路径显示(只显示最后两级,避免过长)
// -------------------------------------------------------------
String _getDisplayPath() {
if (_currentPath == '/') return '根目录';
final parts = _currentPath.split('/').where((s) => s.isNotEmpty).toList();
if (_currentPath == '/' || _currentPath.isEmpty) return '根目录';
final decoded = _safeDecode(_currentPath);
final parts = decoded.split('/').where((s) => s.isNotEmpty).toList();
if (parts.isEmpty) return '根目录';
if (parts.length <= 2) return parts.join(' / ');
return '... / ${parts.sublist(parts.length - 2).join(' / ')}';
}
@@ -132,85 +125,74 @@ class _WebDAVFileListPageState extends State<WebDAVFileListPage> {
),
],
),
body: _buildBody(),
);
}
// -------------------------------------------------------------
// 构建主体内容
// -------------------------------------------------------------
Widget _buildBody() {
if (_isLoading) {
return const Center(
child: CircularProgressIndicator(
color: Color(0xFFB8D4D0),
),
);
}
if (_errorMessage.isNotEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 48, color: Colors.grey[600]),
const SizedBox(height: 16),
Text(
_errorMessage,
style: TextStyle(color: Colors.grey[400]),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadDirectory,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFB8D4D0),
foregroundColor: Colors.black87,
body: _isLoading
? const Center(
child: CircularProgressIndicator(
color: Color(0xFFB8D4D0),
),
child: const Text('重试'),
),
],
),
);
}
if (_items.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.folder_open, size: 48, color: Colors.grey[600]),
const SizedBox(height: 16),
Text(
'此目录为空',
style: TextStyle(color: Colors.grey[400]),
),
const SizedBox(height: 8),
Text(
'支持格式: MP3, FLAC, M4A, APE, WAV, OPUS',
style: TextStyle(color: Colors.grey[600], fontSize: 12),
),
],
),
);
}
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
itemCount: _items.length,
itemBuilder: (context, index) {
final item = _items[index];
return _buildListItem(item);
},
)
: _errorMessage.isNotEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline,
size: 48, color: Colors.grey[600]),
const SizedBox(height: 16),
Text(
_errorMessage,
style: TextStyle(color: Colors.grey[400]),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _loadDirectory,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFB8D4D0),
foregroundColor: Colors.black87,
),
child: const Text('重试'),
),
],
),
)
: _items.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.folder_open,
size: 48, color: Colors.grey[600]),
const SizedBox(height: 16),
Text(
'此目录为空',
style: TextStyle(color: Colors.grey[400]),
),
const SizedBox(height: 8),
Text(
'支持格式: MP3, FLAC, M4A, APE, WAV, OPUS',
style: TextStyle(
color: Colors.grey[600], fontSize: 12),
),
],
),
)
: ListView.builder(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8),
itemCount: _items.length,
itemBuilder: (context, index) {
final item = _items[index];
return _buildListItem(item);
},
),
);
}
// -------------------------------------------------------------
// 构建单个列表项(区分目录和文件)
// -------------------------------------------------------------
Widget _buildListItem(WebDAVItem item) {
final displayName = _safeDecode(item.name);
if (item.isDirectory) {
// ---------- 目录项 ----------
return ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
leading: const Icon(
@@ -219,7 +201,7 @@ class _WebDAVFileListPageState extends State<WebDAVFileListPage> {
size: 32,
),
title: Text(
item.name,
displayName,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
@@ -242,7 +224,6 @@ class _WebDAVFileListPageState extends State<WebDAVFileListPage> {
onTap: () => _enterDirectory(item),
);
} else {
// ---------- 音乐文件项 ----------
final sizeStr = item.size != null
? '${(item.size! / 1024 / 1024).toStringAsFixed(1)} MB'
: '';
@@ -254,7 +235,7 @@ class _WebDAVFileListPageState extends State<WebDAVFileListPage> {
size: 28,
),
title: Text(
item.name,
displayName,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,