diff --git a/tdesign-component/example/ios/Podfile.lock b/tdesign-component/example/ios/Podfile.lock index dfbe2678b..63d34ad33 100644 --- a/tdesign-component/example/ios/Podfile.lock +++ b/tdesign-component/example/ios/Podfile.lock @@ -1,22 +1,28 @@ PODS: - Flutter (1.0.0) + - image_picker_ios (0.0.1): + - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS DEPENDENCIES: - Flutter (from `Flutter`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) EXTERNAL SOURCES: Flutter: :path: Flutter + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" SPEC CHECKSUMS: - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + image_picker_ios: b545a5f16c0fa88e3ecbbce3ed4de45567a8ec18 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 PODFILE CHECKSUM: cf0c950f7e9a456b4e325f5b8fc0f98906a3705a diff --git a/tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj b/tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj index f23da4eb8..51726cf90 100644 --- a/tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj +++ b/tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj @@ -152,7 +152,7 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - EF2485925A6014C2D38ED73D /* [CP] Copy Pods Resources */, + BDA91D8CAA844B388F3140CD /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -169,7 +169,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -264,7 +264,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - EF2485925A6014C2D38ED73D /* [CP] Copy Pods Resources */ = { + BDA91D8CAA844B388F3140CD /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( diff --git a/tdesign-component/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/tdesign-component/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a33..a6b826db2 100644 --- a/tdesign-component/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/tdesign-component/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ > exampleMap = { ExamplePageModel( text: 'Upload 上传', name: 'upload', - isTodo: true, - pageBuilder: _wrapInheritedTheme((context) => const TodoPage())), + pageBuilder: _wrapInheritedTheme((context) => const TDUploadPage())), ], '数据展示': [ ExamplePageModel( diff --git a/tdesign-component/example/lib/page/td_upload_page.dart b/tdesign-component/example/lib/page/td_upload_page.dart new file mode 100644 index 000000000..1572c781a --- /dev/null +++ b/tdesign-component/example/lib/page/td_upload_page.dart @@ -0,0 +1,202 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDUploadPage extends StatefulWidget { + const TDUploadPage({Key? key}) : super(key: key); + + @override + State createState() => TDUploadState(); +} + +class TDUploadState extends State { + final List files1 = []; + final List files2 = [ + TDUploadFile( + key: 1, + remotePath: + 'https://tdesign.gtimg.com/miniprogram/images/example4.png'), + TDUploadFile( + key: 2, + remotePath: + 'https://tdesign.gtimg.com/miniprogram/images/example6.png'), + TDUploadFile( + key: 3, + remotePath: + 'https://tdesign.gtimg.com/miniprogram/images/example5.png'), + ]; + final List files3 = [ + TDUploadFile( + key: 1, + status: TDUploadFileStatus.loading, + loadingText: '上传中...', + remotePath: + 'https://tdesign.gtimg.com/miniprogram/images/example5.png'), + TDUploadFile( + key: 2, + status: TDUploadFileStatus.loading, + progress: 68, + remotePath: + 'https://tdesign.gtimg.com/miniprogram/images/example4.png'), + ]; + final List files4 = [ + TDUploadFile( + key: 1, + status: TDUploadFileStatus.retry, + retryText: '重新上传', + remotePath: + 'https://tdesign.gtimg.com/miniprogram/images/example4.png'), + ]; + final List files5 = [ + TDUploadFile( + key: 1, + status: TDUploadFileStatus.error, + errorText: '上传失败', + remotePath: + 'https://tdesign.gtimg.com/miniprogram/images/example4.png'), + ]; + + void onVaueChanged(List fileList, List value, + TDUploadType event) { + switch (event) { + case TDUploadType.add: + setState(() { + fileList.addAll(value); + }); + break; + case TDUploadType.remove: + setState(() { + fileList.removeWhere((element) => element.key == value[0].key); + }); + break; + } + } + + void onClick(int key) { + print('点击 $key'); + } + + void onCancel() { + print('取消'); + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + exampleCodeGroup: 'upload', + desc: '上传组件', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '单选上传', builder: _uploadSingle), + ExampleItem(desc: '多选上传', builder: _uploadMultiple), + ], + ), + ExampleModule( + title: '组件状态', + children: [ + ExampleItem(desc: '加载状态', builder: _uploadLoading), + ExampleItem(desc: '重新上传', builder: _uploadRetry), + ExampleItem(desc: '上传失败', builder: _uploadError), + ], + ), + ]); + } + + Widget wrapDemoContainer(String title, {required Widget child}) { + return Container( + padding: const EdgeInsets.all(16), + decoration: const BoxDecoration(color: Colors.white), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TDText( + title, + style: const TextStyle(fontSize: 16), + ), + const SizedBox( + height: 16, + ), + child + ], + ), + ); + } + + @Demo(group: 'upload') + Widget _uploadSingle(BuildContext context) { + return wrapDemoContainer('单选上传', + child: TDUpload( + files: files1, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onVaueChanged(files1, files, type)), + )); + } + + @Demo(group: 'upload') + Widget _uploadMultiple(BuildContext context) { + return wrapDemoContainer('多选上传', + child: TDUpload( + files: files2, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onVaueChanged(files2, files, type)), + )); + } + + @Demo(group: 'upload') + Widget _uploadLoading(BuildContext context) { + return wrapDemoContainer('上传图片', + child: TDUpload( + files: files3, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onVaueChanged(files3, files, type)), + )); + } + + @Demo(group: 'upload') + Widget _uploadRetry(BuildContext context) { + return wrapDemoContainer('上传图片', + child: TDUpload( + files: files4, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onVaueChanged(files4, files, type)), + )); + } + + @Demo(group: 'upload') + Widget _uploadError(BuildContext context) { + return wrapDemoContainer('上传图片', + child: TDUpload( + files: files5, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onVaueChanged(files5, files, type)), + )); + } +} diff --git a/tdesign-component/example/pubspec.lock b/tdesign-component/example/pubspec.lock index 3bc1c1e38..0d4a96eaf 100644 --- a/tdesign-component/example/pubspec.lock +++ b/tdesign-component/example/pubspec.lock @@ -45,10 +45,18 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e + url: "https://pub.dev" + source: hosted + version: "0.3.3+8" cupertino_icons: dependency: "direct main" description: @@ -81,6 +89,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" + url: "https://pub.dev" + source: hosted + version: "0.9.2+1" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385 + url: "https://pub.dev" + source: hosted + version: "0.9.4" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: "0aa47a725c346825a2bd396343ce63ac00bda6eff2fbc43eabe99737dede8262" + url: "https://pub.dev" + source: hosted + version: "2.6.1" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "2ad726953f6e8affbc4df8dc78b77c3b4a060967a291e528ef72ae846c60fb69" + url: "https://pub.dev" + source: hosted + version: "0.9.3+2" flutter: dependency: "direct main" description: flutter @@ -115,6 +155,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.15" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" + url: "https://pub.dev" + source: hosted + version: "2.0.19" flutter_screenutil: dependency: "direct main" description: @@ -144,6 +192,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" http: dependency: "direct main" description: @@ -160,22 +213,78 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" - intl: - dependency: "direct main" + image_picker: + dependency: transitive description: - name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + name: image_picker + sha256: "1f498d086203360cca099d20ffea2963f48c39ce91bdd8a3b6d4a045786b02c8" + url: "https://pub.dev" + source: hosted + version: "1.0.8" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "844c6da4e4f2829dffdab97816bca09d0e0977e8dcef7450864aba4e07967a58" url: "https://pub.dev" source: hosted - version: "0.18.0" - js: + version: "0.8.9+6" + image_picker_for_web: dependency: transitive description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + name: image_picker_for_web + sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3 url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "3.0.2" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: "917a5cadd67d052554cfb258595e54217de53fac5b52939426e26319a02e6297" + url: "https://pub.dev" + source: hosted + version: "0.8.9+2" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "0e827c156e3a90edd3bbe7f6de048b39247b16e58173b08a835b7eb00aba239e" + url: "https://pub.dev" + source: hosted + version: "2.9.2" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + url: "https://pub.dev" + source: hosted + version: "0.18.1" lints: dependency: transitive description: @@ -196,26 +305,34 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + url: "https://pub.dev" + source: hosted + version: "1.10.0" + mime: + dependency: transitive + description: + name: mime + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.0.6" path: dependency: transitive description: @@ -297,26 +414,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -344,10 +461,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" typed_data: dependency: transitive description: @@ -372,6 +489,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.3" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" win32: dependency: transitive description: @@ -389,5 +514,5 @@ packages: source: hosted version: "1.0.3" sdks: - dart: ">=3.0.0-0 <4.0.0" - flutter: ">=3.7.0" + dart: ">=3.2.3 <4.0.0" + flutter: ">=3.16.6" diff --git a/tdesign-component/lib/src/components/upload/td_upload.dart b/tdesign-component/lib/src/components/upload/td_upload.dart new file mode 100644 index 000000000..e57f116f2 --- /dev/null +++ b/tdesign-component/lib/src/components/upload/td_upload.dart @@ -0,0 +1,383 @@ +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:image_picker/image_picker.dart'; + +import '../../../tdesign_flutter.dart'; + + +enum TDUploadMediaType { + image, // 图片 + video, // 视频 +} + +enum TDUploadValidatorError { + overSize, // 超出文件大小 + overQuantity, // 超出文件数量限制 +} + +enum TDUploadFileStatus { + success, // 成功 + loading, // 加载中 + error, // 失败 + retry, // 重试 +} + +enum TDUploadType { + add, // 添加 + remove, // 删除 +} + +class TDUploadFile { + TDUploadFile( + {required this.key, + this.remotePath, + this.assetPath, + this.file, + this.progress, + this.status = TDUploadFileStatus.success, + this.loadingText = 'Loading...', + this.retryText = 'Re-Upload', + this.errorText = 'Error', + this.canDelete = true}); + + final int key; + final String? remotePath; + final String? assetPath; + final File? file; + final bool canDelete; + final int? progress; + final String loadingText; + final String retryText; + final String errorText; + TDUploadFileStatus status; +} + +typedef TDUploadErrorEvent = void Function(Object e); +typedef TDUploadClickEvent = void Function(int value); +typedef TDUploadValueChangedEvent = void Function( + List files, TDUploadType type); +typedef TDUploadValidatorEvent = void Function(TDUploadValidatorError e); + +class TDUpload extends StatefulWidget { + const TDUpload( + {Key? key, + this.max = 0, + this.mediaType = const [TDUploadMediaType.image, TDUploadMediaType.video], + this.sizeLimit, + this.onCancel, + this.onError, + this.onValidate, + this.onClick, + required this.files, + this.onChange, + this.multiple = false}) + : super(key: key); + + /// 控制展示的文件列表 + final List files; + + /// 用于控制文件上传数量,0为不限制,仅在multiple为true时有效 + final int max; + + /// 支持上传的文件类型,图片或视频 + final List mediaType; + + /// 图片大小限制,单位为KB + final double? sizeLimit; + + /// 是否多选上传,默认false + final bool multiple; + + /// 监听取消上传 + final VoidCallback? onCancel; + + /// 监听获取资源错误 + final TDUploadErrorEvent? onError; + + /// 监听文件校验出错 + final TDUploadValidatorEvent? onValidate; + + /// 监听点击图片位 + final TDUploadClickEvent? onClick; + + /// 监听添加或删除照片 + final TDUploadValueChangedEvent? onChange; + + @override + State createState() => _TDUploadState(); +} + +class _TDUploadState extends State { + List fileList = []; + bool get canUpload => widget.multiple + ? (widget.max == 0 ? true : fileList.length < widget.max) + : fileList.isEmpty; + final ImagePicker _picker = ImagePicker(); + + @override + initState() { + super.initState(); + fileList = widget.files; + } + + // 获取相册照片或视频 + Future> getMediaFromPicker() async { + if (!canUpload || widget.mediaType.isEmpty) { + return []; + } + + List medias; + + try { + if (widget.multiple) { + if (widget.mediaType.length == 1 && + widget.mediaType.contains(TDUploadMediaType.image)) { + medias = await _picker.pickMultiImage(); + } else { + medias = await _picker.pickMultiImage(); + } + + return medias; + } + + XFile? media; + if (widget.mediaType.length == 1) { + if (widget.mediaType.contains(TDUploadMediaType.image)) { + media = await _picker.pickImage(source: ImageSource.gallery); + } else { + media = await _picker.pickVideo(source: ImageSource.gallery); + } + } else { + media = await _picker.pickImage(source: ImageSource.gallery); + } + + return media == null ? [] : [media]; + } on PlatformException catch (e) { + if (e.code == 'null-error' && widget.onCancel != null) { + widget.onCancel!(); + } else { + if (widget.onError != null) { + widget.onError!(e); + } + } + } catch (e) { + if (widget.onError != null) { + widget.onError!(e); + } + } + + return []; + } + + // 处理获取到的资源 + void extractImageList(List files) async { + if (!canUpload || files.isEmpty) { + return; + } + + var result = await validateResources(files); + + if (result != null) { + if (widget.onValidate != null) { + widget.onValidate!(result); + } + return; + } + + var originMaxKeys = + fileList.isEmpty ? 0 : fileList.map((file) => file.key).reduce(max); + + var newFiles = []; + for (var i = 0; i < files.length; i++) { + newFiles.add(TDUploadFile( + key: originMaxKeys + i + 1, + file: File(files[i].path), + assetPath: files[i].path)); + } + + if (widget.onChange != null) { + widget.onChange!(newFiles, TDUploadType.add); + } + } + + // 校验资源 + Future validateResources(List files) async { + TDUploadValidatorError? error; + + if (widget.multiple && widget.max > 0) { + var remain = widget.max - fileList.length; + + if (files.length > remain) { + error = TDUploadValidatorError.overQuantity; + return error; + } + } + + for (var file in files) { + if (widget.sizeLimit != null) { + var fileSize = (await file.length()) * 1024; + + if (fileSize > widget.sizeLimit!) { + error = TDUploadValidatorError.overSize; + break; + } + } + } + + return error; + } + + // 删除资源 + void onDelete(TDUploadFile file) { + if (widget.onChange != null) { + widget.onChange!([file], TDUploadType.remove); + } + } + + @override + Widget build(BuildContext context) { + return SizedBox( + width: double.infinity, + child: Wrap( + spacing: 8, + runSpacing: 16, + children: [ + ...fileList.map((file) => _buildImageBox(context, file)).toList(), + _buildUploadBox(context, shouldDisplay: canUpload, onTap: () async { + final files = await getMediaFromPicker(); + extractImageList(files); + }), + ], + ), + ); + } + + Widget _buildUploadBox(BuildContext context, + {void Function()? onTap, bool shouldDisplay = true}) { + return Visibility( + visible: shouldDisplay, + child: GestureDetector( + onTap: onTap, + child: Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.circular(6)), + child: const Center( + child: Icon( + TDIcons.add, + color: Color.fromRGBO(0, 0, 0, 0.4), + size: 28, + )), + ))); + } + + Widget _buildImageBox(BuildContext context, TDUploadFile file) { + return GestureDetector( + onTap: () { + if (widget.onClick != null) { + widget.onClick!(file.key); + } + }, + child: Stack( + children: [ + TDImage( + width: 80, + height: 80, + imgUrl: file.remotePath, + assetUrl: file.assetPath, + ), + Visibility( + visible: file.status != TDUploadFileStatus.success, + child: _buildShadowBox(file)), + Visibility( + visible: file.canDelete, + child: Positioned( + right: 0, + top: 0, + child: GestureDetector( + onTap: () { + onDelete(file); + }, + child: Container( + width: 20, + height: 20, + decoration: const BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.6), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(6), + topRight: Radius.circular(6))), + child: const Center( + child: Icon( + TDIcons.close, + size: 16, + color: Colors.white, + )), + ), + ))) + ], + ), + ); + } + + Widget _buildShadowBox(TDUploadFile file) { + var displayText = ''; + switch (file.status) { + case TDUploadFileStatus.loading: + displayText = + file.progress != null ? '${file.progress!}%' : file.loadingText; + break; + case TDUploadFileStatus.retry: + displayText = file.retryText; + break; + case TDUploadFileStatus.error: + displayText = file.errorText; + break; + default: + } + + return Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: const Color.fromRGBO(0, 0, 0, 0.4), + borderRadius: BorderRadius.circular(6)), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Visibility( + visible: file.status == TDUploadFileStatus.loading, + child: const TDLoading( + size: TDLoadingSize.large, + icon: TDLoadingIcon.circle, + iconColor: Colors.white, + ), + ), + Visibility( + visible: file.status == TDUploadFileStatus.retry || + file.status == TDUploadFileStatus.error, + child: Icon( + file.status == TDUploadFileStatus.retry + ? TDIcons.refresh + : TDIcons.close_circle, + size: 24, + color: Colors.white, + )), + TDText( + displayText, + textColor: Colors.white, + style: const TextStyle(fontSize: 12, height: 1.67), + ), + ], + ), + ), + ), + ); + } +} diff --git a/tdesign-component/lib/tdesign_flutter.dart b/tdesign-component/lib/tdesign_flutter.dart index bbead3630..c827559a8 100644 --- a/tdesign-component/lib/tdesign_flutter.dart +++ b/tdesign-component/lib/tdesign_flutter.dart @@ -78,6 +78,7 @@ export 'src/components/time_counter/td_time_counter_controller.dart'; export 'src/components/time_counter/td_time_counter_style.dart'; export 'src/components/toast/td_toast.dart'; export 'src/components/tree/td_tree_select.dart'; +export 'src/components/upload/td_upload.dart'; export 'src/theme/basic.dart'; export 'src/theme/resource_delegate.dart'; export 'src/theme/td_colors.dart'; diff --git a/tdesign-component/pubspec.lock b/tdesign-component/pubspec.lock index 1a1978a1c..03c18a440 100644 --- a/tdesign-component/pubspec.lock +++ b/tdesign-component/pubspec.lock @@ -37,10 +37,18 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e + url: "https://pub.dev" + source: hosted + version: "0.3.3+8" fake_async: dependency: transitive description: @@ -49,6 +57,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" + url: "https://pub.dev" + source: hosted + version: "0.9.2+1" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385 + url: "https://pub.dev" + source: hosted + version: "0.9.4" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b + url: "https://pub.dev" + source: hosted + version: "2.6.2" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "2ad726953f6e8affbc4df8dc78b77c3b4a060967a291e528ef72ae846c60fb69" + url: "https://pub.dev" + source: hosted + version: "0.9.3+2" flutter: dependency: "direct main" description: flutter @@ -70,6 +110,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" + url: "https://pub.dev" + source: hosted + version: "2.0.19" flutter_slidable: dependency: "direct main" description: @@ -91,14 +139,91 @@ packages: description: flutter source: sdk version: "0.0.0" - js: + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + url: "https://pub.dev" + source: hosted + version: "1.2.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "1f498d086203360cca099d20ffea2963f48c39ce91bdd8a3b6d4a045786b02c8" + url: "https://pub.dev" + source: hosted + version: "1.0.8" + image_picker_android: dependency: transitive description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + name: image_picker_android + sha256: "844c6da4e4f2829dffdab97816bca09d0e0977e8dcef7450864aba4e07967a58" url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.8.9+6" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3 + url: "https://pub.dev" + source: hosted + version: "3.0.2" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: "917a5cadd67d052554cfb258595e54217de53fac5b52939426e26319a02e6297" + url: "https://pub.dev" + source: hosted + version: "0.8.9+2" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b + url: "https://pub.dev" + source: hosted + version: "2.9.3" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" lints: dependency: transitive description: @@ -111,26 +236,34 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + url: "https://pub.dev" + source: hosted + version: "1.10.0" + mime: + dependency: transitive + description: + name: mime + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.0.6" path: dependency: transitive description: @@ -139,6 +272,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" sky_engine: dependency: transitive description: flutter @@ -148,26 +289,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -188,10 +329,18 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.3.2" vector_math: dependency: transitive description: @@ -200,6 +349,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" sdks: - dart: ">=3.0.0-0 <4.0.0" - flutter: ">=3.7.0" + dart: ">=3.2.3 <4.0.0" + flutter: ">=3.16.6" diff --git a/tdesign-component/pubspec.yaml b/tdesign-component/pubspec.yaml index 276ba7b99..6f27fe08f 100644 --- a/tdesign-component/pubspec.yaml +++ b/tdesign-component/pubspec.yaml @@ -20,8 +20,8 @@ dependencies: # 滑动单元格组件 flutter_slidable: 3.1.0 -# # 相册选择组件 -# image_picker: 1.1.2 + # 相册选择组件 + image_picker: 1.0.8 dev_dependencies: flutter_lints: ^1.0.4