feat: inventory and inout info develop

This commit is contained in:
louis 2024-03-26 15:30:43 +08:00
parent fc0187acf0
commit cf51cd8ada
26 changed files with 666 additions and 292 deletions

View File

@ -1,99 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:sk_base_mobile/constants/global_url.dart';
import 'package:sk_base_mobile/models/base_response.dart';
import 'package:sk_base_mobile/models/dict_type.model.dart';
import 'package:sk_base_mobile/services/dio.service.dart';
import '../constants/constants.dart';
//
Future<Response> login(String username, String password) {
return DioService.dio.post(
Urls.login,
data: {'username': username, 'password': password},
);
}
//
Future<Response> getUserInfo() {
return DioService.dio.get(
Urls.getUserInfo,
);
}
//
Future<Response<PaginationData>> getProjects(Map params) {
return DioService.dio.get<PaginationData>(Urls.getProjects,
queryParameters: {'page': 1, 'pageSize': 10, ...params});
}
//
Future<Response<PaginationData>> getProducts(Map params) {
return DioService.dio.get<PaginationData>(Urls.getProducts,
queryParameters: {'page': 1, 'pageSize': 10, ...params});
}
//
Future<Response<PaginationData>> getInventoryInout(Map params) {
return DioService.dio.get<PaginationData>(Urls.inventoryInout,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
//
Future<Response> createInventoryInout(Map params) {
return DioService.dio.post(Urls.inventoryInout, data: params);
}
//
Future<Response> updateInventoryInout(int id, Map params) {
return DioService.dio.put('${Urls.inventoryInout}/$id', data: params);
}
//
Future<Response<PaginationData>> getInventory(Map params) {
return DioService.dio.get<PaginationData>(Urls.inventory,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
// ()
Future<Response> getDictTypeAll(Map params) {
return DioService.dio.post(Urls.getDictType, data: params);
}
Future<Response> logout() {
return DioService.dio.post(
Urls.logout,
);
}
Future<Response> deleteAccount() {
return DioService.dio.post(
Urls.deleteAccount,
);
}
Future<Response> getAppConfig() {
Map<String, dynamic> query = {'ver': 0};
return DioService.dio.get(Urls.getAppConfig, queryParameters: query);
}
Future<Response> saveUserInfo(Map<String, dynamic> data) {
return DioService.dio.post(Urls.saveUserInfo, data: data);
}
Future<Response> uploadImg(File file,
{String? bussinessModule, String? bussinessRecordId}) async {
String fileName = file.path.split('/').last;
final filePath = await MultipartFile.fromFile(file.path, filename: fileName);
final formData = FormData.fromMap({
'bussinessRecordId': bussinessRecordId,
'bussinessModule': bussinessModule,
'file': filePath,
});
return DioService.dio.post(Urls.uploadAttachemnt, data: formData);
}
Future<Response> updateAvatar(String filename) {
return DioService.dio.post(Urls.updateAvatar, data: {'avatarPath': filename});
}

110
lib/apis/index.dart Normal file
View File

@ -0,0 +1,110 @@
import 'dart:async';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:sk_base_mobile/constants/global_url.dart';
import 'package:sk_base_mobile/models/base_response.dart';
import 'package:sk_base_mobile/models/dict_type.model.dart';
import 'package:sk_base_mobile/services/dio.service.dart';
import '../constants/constants.dart';
class Api {
//
static Future<Response> login(String username, String password) {
return DioService.dio.post(
Urls.login,
data: {'username': username, 'password': password},
);
}
//
static Future<Response> getUserInfo() {
return DioService.dio.get(
Urls.getUserInfo,
);
}
//
static Future<Response<PaginationData>> getProjects(Map params) {
return DioService.dio.get<PaginationData>(Urls.projects,
queryParameters: {'page': 1, 'pageSize': 10, ...params});
}
//
static Future<Response<PaginationData>> getProducts(Map params) {
return DioService.dio.get<PaginationData>(Urls.products,
queryParameters: {'page': 1, 'pageSize': 10, ...params});
}
//
static Future<Response<PaginationData>> getInventoryInout(Map params) {
return DioService.dio.get<PaginationData>(Urls.inventoryInout,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
//
static Future<Response> getInventoryInOutInfo(int id) {
return DioService.dio.get(
'${Urls.inventoryInout}/$id',
);
}
//
static Future<Response> createInventoryInout(Map params) {
return DioService.dio.post(Urls.inventoryInout, data: params);
}
//
static Future<Response> updateInventoryInout(int id, Map params) {
return DioService.dio.put('${Urls.inventoryInout}/$id', data: params);
}
//
static Future<Response<PaginationData>> getInventory(Map params) {
return DioService.dio.get<PaginationData>(Urls.inventory,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
// ()
static Future<Response> getDictTypeAll(Map params) {
return DioService.dio.post(Urls.getDictType, data: params);
}
static Future<Response> logout() {
return DioService.dio.post(
Urls.logout,
);
}
static Future<Response> deleteAccount() {
return DioService.dio.post(
Urls.deleteAccount,
);
}
static Future<Response> getAppConfig() {
Map<String, dynamic> query = {'ver': 0};
return DioService.dio.get(Urls.getAppConfig, queryParameters: query);
}
static Future<Response> saveUserInfo(Map<String, dynamic> data) {
return DioService.dio.post(Urls.saveUserInfo, data: data);
}
static Future<Response> uploadImg(File file,
{String? bussinessModule, String? bussinessRecordId}) async {
String fileName = file.path.split('/').last;
final filePath =
await MultipartFile.fromFile(file.path, filename: fileName);
final formData = FormData.fromMap({
'bussinessRecordId': bussinessRecordId,
'bussinessModule': bussinessModule,
'file': filePath,
});
return DioService.dio.post(Urls.uploadAttachemnt, data: formData);
}
static Future<Response> updateAvatar(String filename) {
return DioService.dio
.post(Urls.updateAvatar, data: {'avatarPath': filename});
}
}

View File

@ -6,8 +6,8 @@ class Urls {
static String deleteAccount = 'user/deleteAccount';
static String saveUserInfo = 'user/saveUserInfo';
static String getUserInfo = 'account/profile';
static String getProjects = 'project';
static String getProducts = 'product';
static String projects = 'project';
static String products = 'product';
static String inventoryInout = 'materials-in-out';
static String inventory = 'materials-inventory';
static String updateAvatar = 'user/updateAvatar';

View File

@ -1,6 +1,7 @@
import 'package:sk_base_mobile/constants/enum.dart';
import 'package:sk_base_mobile/models/file.model.dart';
import 'package:sk_base_mobile/models/index.dart';
import 'package:sk_base_mobile/models/inventory.model.dart';
import 'package:sk_base_mobile/models/product.model.dart';
import 'package:sk_base_mobile/models/project.model.dart';
@ -24,6 +25,7 @@ class InventoryInOutModel {
required this.files,
required this.project,
required this.product,
this.inventory,
});
final int? id;
@ -41,10 +43,10 @@ class InventoryInOutModel {
final String? remark;
final int? projectId;
final int? isDelete;
final List<FileModel?> files;
final List<FileModel> files;
final ProjectModel? project;
final ProductModel? product;
final InventoryModel? inventory;
factory InventoryInOutModel.fromJson(Map<String, dynamic> json) {
return InventoryInOutModel(
id: json["id"],
@ -72,6 +74,9 @@ class InventoryInOutModel {
product: json["product"] == null
? null
: ProductModel.fromJson(json["product"]),
inventory: json['inventory'] == null
? null
: InventoryModel.fromJson(json["inventory"]),
);
}
@ -94,5 +99,6 @@ class InventoryInOutModel {
"files": files.map((x) => x).toList(),
"project": project?.toJson(),
"product": product?.toJson(),
"inventory": inventory?.toJson(),
};
}

View File

@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_search.dart';
class InventoryPage extends StatelessWidget {
const InventoryPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('库存管理')),
body: InventorySearch(),
);
}
}

View File

@ -16,7 +16,12 @@ class InventoryInoutCard extends StatelessWidget {
final controller = Get.find<InventoryInoutController>();
@override
Widget build(BuildContext context) {
return Container(
return InkWell(
onTap: () {
controller
.showInventoryInoutInfoDialog(controller.list[ind][index].id!);
},
child: Container(
margin: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(15),
horizontal: ScreenAdaper.width(15)),
@ -149,6 +154,7 @@ class InventoryInoutCard extends StatelessWidget {
// ),
],
),
),
);
}
@ -224,9 +230,8 @@ class InventoryInoutCard extends StatelessWidget {
child: FadeInCacheImage(
width: ScreenAdaper.width(100),
height: ScreenAdaper.width(100),
url: controller.list[ind][index].product?.files.isNotEmpty ??
false
? '${GloablConfig.OSS_URL}${controller.list[ind][index].product?.files[0].path}'
url: controller.list[ind][index].files.isNotEmpty
? '${GloablConfig.OSS_URL}${controller.list[ind][index].files[0]?.path}'
: ''),
));
}

View File

@ -0,0 +1,190 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/config.dart';
import 'package:sk_base_mobile/constants/bg_color.dart';
import 'package:sk_base_mobile/constants/constants.dart';
import 'package:sk_base_mobile/models/index.dart';
import 'package:sk_base_mobile/models/inventory.model.dart';
import 'package:sk_base_mobile/util/date.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/empty.dart';
import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
import 'package:sk_base_mobile/widgets/image_preview.dart';
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
class InventoryInoutInfo extends StatelessWidget {
late int inventoryInoutId;
late InventoryInouInfoController controller;
InventoryInoutInfo({Key? key, required this.inventoryInoutId})
: controller = Get.put(InventoryInouInfoController(inventoryInoutId)),
super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
child: Column(children: [
Container(
padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.width(10),
vertical: ScreenAdaper.height(15)),
decoration: const BoxDecoration(color: AppTheme.primaryColor),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'出入库详情',
style: TextStyle(
fontSize: ScreenAdaper.sp(25), color: AppTheme.nearlyWhite),
),
],
),
),
Expanded(
child: Obx(() => controller.inventoryInoutInfo.value == null
? const Center(child: LoadingIndicator(common: true))
: SingleChildScrollView(
child: Column(
children: [
buildListItem(
leading: '出入库类型',
trailing: InventoryInOrOutEnum.In ==
controller.inventoryInoutInfo.value?.inOrOut
? '入库'
: '出库'),
customDivider(),
if (InventoryInOrOutEnum.In ==
controller.inventoryInoutInfo.value?.inOrOut) ...[
buildListItem(
leading: '存放位置',
trailing: controller
.inventoryInoutInfo.value?.inventory?.position),
customDivider(),
],
buildListItem(
leading: '出入库时间',
trailing: DateUtil.format(
controller.inventoryInoutInfo.value!.time!)),
customDivider(),
buildListItem(
leading: '所属公司',
trailing: controller.inventoryInoutInfo.value?.product
?.company?.name),
customDivider(),
buildListItem(
leading: '产品名称',
trailing: controller
.inventoryInoutInfo.value?.product?.name),
customDivider(),
buildListItem(
leading: '产品规格',
trailing: controller.inventoryInoutInfo.value?.product
?.productSpecification),
customDivider(),
buildListItem(
leading: '数量',
trailing:
'${controller.inventoryInoutInfo.value?.quantity}${controller.inventoryInoutInfo.value?.product?.unit?.label ?? ''}'),
customDivider(),
buildListItem(
leading: '单价',
trailing:
'${double.parse('${controller.inventoryInoutInfo.value?.unitPrice ?? 0}')}'),
customDivider(),
buildListItem(
leading: '操作人',
trailing: controller.inventoryInoutInfo.value?.agent),
customDivider(),
buildListItem(
leading: '备注',
trailing:
controller.inventoryInoutInfo.value?.remark),
customDivider(),
buildListItem(
leading: '照片',
),
buildAttachmentsPreview()
],
))))
]),
);
}
Widget buildAttachmentsPreview() {
return Container(
padding: EdgeInsets.symmetric(vertical: ScreenAdaper.height(20)),
child: Wrap(
spacing: ScreenAdaper.width(10),
runSpacing: ScreenAdaper.height(10),
children: (controller.inventoryInoutInfo.value?.files ?? [])
.map((e) => buildImage(e.path))
.toList(),
),
);
}
Widget buildImage(String? path) {
return path == null
? const SizedBox()
: InkWell(
onTap: () => {
if (controller.inventoryInoutInfo.value!.files.isNotEmpty)
{
Get.dialog(ImagePreivew(
galleryItems: controller.inventoryInoutInfo.value!.files,
initialIndex: controller.inventoryInoutInfo.value!.files
.indexWhere((element) => element.path == path)))
}
},
child: Container(
margin: EdgeInsets.only(
right: ScreenAdaper.width(defaultPadding) / 2),
child: ClipRRect(
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
child: FadeInCacheImage(
width: ScreenAdaper.width(300),
height: ScreenAdaper.width(300),
url: '${GloablConfig.OSS_URL}$path'),
)),
);
}
Widget customDivider() {
return Divider(
height: ScreenAdaper.height(1),
color: AppTheme.dividerColor,
);
}
Widget buildListItem({String? leading, dynamic trailing}) {
return ListTile(
leading: Text(
'${leading ?? ''}: ',
style: TextStyle(fontSize: ScreenAdaper.sp(25)),
),
trailing: Text('${trailing ?? ''}',
style: TextStyle(fontSize: ScreenAdaper.sp(25))));
}
}
class InventoryInouInfoController extends GetxController {
final inventoryInoutInfo = Rx<InventoryInOutModel?>(null);
int inventoryInoutId;
InventoryInouInfoController(this.inventoryInoutId);
@override
onReady() {
getData();
}
Future<void> getData() async {
printInfo(info: '$inventoryInoutId');
await Future.delayed(const Duration(milliseconds: 500));
final res = await Api.getInventoryInOutInfo(inventoryInoutId);
if (res.data != null) {
inventoryInoutInfo.value = InventoryInOutModel.fromJson(res.data);
}
}
}

View File

@ -1,14 +1,15 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/constants/enum.dart';
import 'package:sk_base_mobile/db_helper/dbHelper.dart';
import 'package:sk_base_mobile/screens/inventory_inout/components/inventory_inout_info.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout.dart';
import 'package:sk_base_mobile/util/date.util.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
import 'package:sk_base_mobile/util/modal.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
import '../../models/index.dart';
import 'components/responsive.dart';
@ -243,6 +244,14 @@ class InventoryInoutController extends GetxController {
);
}
Future showInventoryInoutInfoDialog(int id) async {
ModalUtil.showGeneralDialog(
width: ScreenAdaper.width(Get.width - 100),
height: ScreenAdaper.height(Get.height - 100),
content: InventoryInoutInfo(inventoryInoutId: id))
.then((value) => {Get.delete<InventoryInouInfoController>()});
}
Future<List<InventoryInOutModel>> getInoutHistory() async {
final res = await Api.getInventoryInout({});
printInfo(info: res.toString());

View File

@ -21,7 +21,9 @@ class LandingPage extends StatelessWidget {
child: Obx(() => Scaffold(
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
floatingActionButton: controller.currentIndex.value == 0
floatingActionButton: [0].indexWhere(
(item) => item == controller.currentIndex.value) >
-1
? FloatingCreateButton()
: null,
bottomNavigationBar: BottomNavBar(),

View File

@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/models/user_info.model.dart';
import 'package:sk_base_mobile/store/auth.store.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
import 'package:sk_base_mobile/apis/index.dart';
class UserInfoController extends GetxController {
final nickNameController = TextEditingController(text: '');

View File

@ -3,13 +3,13 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/constants/bg_color.dart';
import 'package:sk_base_mobile/models/index.dart';
import 'package:sk_base_mobile/models/inventory.model.dart';
import 'package:sk_base_mobile/util/debouncer.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
class InventorySearch extends StatelessWidget {
Function(InventoryModel)? onInventorySelected;

View File

@ -3,12 +3,12 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/constants/bg_color.dart';
import 'package:sk_base_mobile/models/index.dart';
import 'package:sk_base_mobile/util/debouncer.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
class ProductSearch extends StatelessWidget {
Function(ProductModel)? onProductSelected;

View File

@ -229,7 +229,8 @@ class NewInventoryInout extends StatelessWidget {
controller.payload['productId'] = inventory.product?.id;
},
),
width: Get.width - ScreenAdaper.width(50));
width: Get.width - ScreenAdaper.width(50))
.then((value) => Get.delete<InventorySearchController>());
}
});
}

View File

@ -4,11 +4,11 @@ import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/constants/enum.dart';
import 'package:sk_base_mobile/db_helper/dbHelper.dart';
import 'package:sk_base_mobile/models/index.dart';
import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout_controller.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
import 'package:sk_base_mobile/util/date.util.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
import 'package:sk_base_mobile/util/media_util.dart';

View File

@ -7,8 +7,9 @@ import 'package:package_info/package_info.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/models/app_bottom_nav_item.dart';
import 'package:sk_base_mobile/models/app_config.dart';
import 'package:sk_base_mobile/screens/inventory/inventory.dart';
import 'package:sk_base_mobile/screens/inventory_inout/inventory_inout.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/screens/workbench/workbench.dart';
import 'package:sk_base_mobile/services/service.dart';
import 'package:sk_base_mobile/store/auth.store.dart';
@ -36,7 +37,7 @@ class AppInfoService extends GetxService {
icon: Icons.inventory_outlined,
activeIcon: Icons.inventory_rounded,
label: '库存',
page: InvenotryInoutPage()),
page: InventoryPage()),
AppBottomNavItem(
icon: Icons.widgets_outlined,
activeIcon: Icons.widgets_rounded,

View File

@ -16,7 +16,7 @@ class DioService extends Get.GetxService {
static late Dio _dio;
List<String> whiteList = [Urls.login];
BaseOptions dioBaseOptions = BaseOptions(
connectTimeout: const Duration(seconds: 5),
connectTimeout: const Duration(seconds: 20),
baseUrl: '${GloablConfig.BASE_URL}',
followRedirects: true);

View File

@ -2,7 +2,7 @@ import 'dart:convert';
import 'package:dio/dio.dart' as Dio;
import 'package:get/get.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
import 'package:sk_base_mobile/widgets/tap_to_dismiss_keyboard.dart';
import 'package:sk_base_mobile/models/auth.dart';
@ -53,7 +53,6 @@ class AuthStore extends GetxController {
}
Future<void> logout({bool force = false}) async {
LoadingUtil.to.show(status: 'Logout...');
await StorageService.to.remove(CacheKeys.token, isWithUser: false);
await StorageService.to.remove(CacheKeys.userInfo, isWithUser: false);
try {

View File

@ -3,7 +3,7 @@ import 'package:get/get_state_manager/get_state_manager.dart';
import 'package:sk_base_mobile/constants/dict_enum.dart';
import 'package:sk_base_mobile/models/dict_item.model.dart';
import 'package:sk_base_mobile/models/dict_type.model.dart';
import 'package:sk_base_mobile/apis/api.dart' as Api;
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
const needCachedKey = [

View File

@ -4,9 +4,8 @@ import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class LoadingUtil extends GetxController {
class LoadingUtil extends GetxService {
static LoadingUtil get to => Get.find();
OverlayEntry? _loadingOverlay;
Future<LoadingUtil> init() async {
@ -53,9 +52,10 @@ class LoadingUtil extends GetxController {
));
},
);
if (Get.overlayContext != null) {
Overlay.of(Get.overlayContext!).insert(_loadingOverlay!);
}
}
hideLoading() {
_loadingOverlay?.remove();

View File

@ -1,6 +1,6 @@
import 'dart:io';
import 'package:dio/dio.dart' as Dio;
import 'package:sk_base_mobile/apis/api.dart' as Api;
import 'package:sk_base_mobile/apis/index.dart';
import 'package:image_picker/image_picker.dart';
import 'package:sk_base_mobile/models/upload_result.model.dart';
import 'package:sk_base_mobile/services/service.dart';
@ -15,7 +15,7 @@ class MediaUtil {
}
AppInfoService.to.isCameraing.value = true;
pickedFile = await ImagePicker()
.pickImage(source: ImageSource.camera, maxWidth: maxWidth ?? 400);
.pickImage(source: ImageSource.camera, maxWidth: maxWidth ?? 2000);
AppInfoService.to.isCameraing.value = false;
} catch (e) {
AppInfoService.to.isCameraing.value = false;

View File

@ -68,7 +68,7 @@ class ModalUtil {
);
static Future<void> showGeneralDialog(
{required Widget content, double? width}) {
{required Widget content, double? width, double? height}) {
return Get.generalDialog(
barrierLabel: "productPicker",
barrierDismissible: true,
@ -79,7 +79,7 @@ class ModalUtil {
borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)),
child: Material(
child: SizedBox(
height: Get.height - ScreenAdaper.height(150),
height: height ?? Get.height - ScreenAdaper.height(150),
width: width ?? Get.width - ScreenAdaper.width(150),
child: content,
))));

View File

@ -0,0 +1,112 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
import 'package:sk_base_mobile/config.dart';
import 'package:sk_base_mobile/models/file.model.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
class ImagePreivew extends StatefulWidget {
ImagePreivew({
super.key,
required this.galleryItems,
this.backgroundDecoration,
this.minScale,
this.maxScale,
this.initialIndex = 0,
this.scrollDirection = Axis.horizontal,
}) : pageController = PageController(initialPage: initialIndex);
final BoxDecoration? backgroundDecoration;
final dynamic minScale;
final dynamic maxScale;
final int initialIndex;
final PageController pageController;
final List<FileModel> galleryItems;
final Axis scrollDirection;
@override
State<ImagePreivew> createState() => _ImagePreivewState();
}
class _ImagePreivewState extends State<ImagePreivew> {
late int currentIndex = widget.initialIndex;
@override
Widget build(BuildContext context) {
return Container(
decoration: widget.backgroundDecoration,
constraints: BoxConstraints.expand(
height: MediaQuery.of(context).size.height,
),
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(),
builder: _buildItem,
itemCount: widget.galleryItems.length,
loadingBuilder: (_, _1) => const LoadingIndicator(common: true),
backgroundDecoration: widget.backgroundDecoration,
pageController: widget.pageController,
onPageChanged: onPageChanged,
scrollDirection: widget.scrollDirection,
),
Container(
padding: const EdgeInsets.all(20.0),
child: Text(
"照片 ${currentIndex + 1}",
style: TextStyle(
decoration: TextDecoration.none,
color: Colors.white,
fontSize: ScreenAdaper.sp(20),
),
),
)
],
),
);
}
PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) {
final FileModel item = widget.galleryItems[index];
return PhotoViewGalleryPageOptions(
onTapUp: (_, _1, _2) {
Get.back();
},
imageProvider: Image.network('${GloablConfig.OSS_URL}${item.path}').image,
initialScale: PhotoViewComputedScale.contained,
minScale: PhotoViewComputedScale.contained * (0.5 + index / 10),
maxScale: PhotoViewComputedScale.covered * 4.1,
heroAttributes: PhotoViewHeroAttributes(tag: '${item.id}'),
);
// return item.isSvg
// ? PhotoViewGalleryPageOptions.customChild(
// child: Container(
// width: 300,
// height: 300,
// child: SvgPicture.asset(
// item.resource,
// height: 200.0,
// ),
// ),
// childSize: const Size(300, 300),
// initialScale: PhotoViewComputedScale.contained,
// minScale: PhotoViewComputedScale.contained * (0.5 + index / 10),
// maxScale: PhotoViewComputedScale.covered * 4.1,
// heroAttributes: PhotoViewHeroAttributes(tag: item.id),
// )
// : PhotoViewGalleryPageOptions(
// imageProvider: AssetImage(item.resource),
// initialScale: PhotoViewComputedScale.contained,
// minScale: PhotoViewComputedScale.contained * (0.5 + index / 10),
// maxScale: PhotoViewComputedScale.covered * 4.1,
// heroAttributes: PhotoViewHeroAttributes(tag: item.id),
// );
}
void onPageChanged(int index) {
setState(() {
currentIndex = index;
});
}
}

View File

@ -1,14 +1,20 @@
import 'package:flutter/cupertino.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class LoadingIndicator extends StatelessWidget {
final bool animating;
const LoadingIndicator({super.key, this.animating = true});
final bool common;
const LoadingIndicator(
{super.key, this.animating = true, this.common = false});
@override
Widget build(BuildContext context) {
return CupertinoActivityIndicator(
return common
? LoadingAnimationWidget.fourRotatingDots(
color: AppTheme.primaryColorLight, size: ScreenAdaper.sp(40))
: CupertinoActivityIndicator(
animating: animating,
color: AppTheme.primaryColor,
radius: ScreenAdaper.sp(20),

View File

@ -1,5 +1,5 @@
import 'dart:io';
import 'package:sk_base_mobile/apis/api.dart' as Api;
import 'package:sk_base_mobile/apis/index.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';

View File

@ -416,6 +416,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.7"
image_gallery_saver:
dependency: "direct main"
description:
name: image_gallery_saver
sha256: "0aba74216a4d9b0561510cb968015d56b701ba1bd94aace26aacdd8ae5761816"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
image_picker:
dependency: "direct main"
description:
@ -680,6 +688,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.2"
photo_view:
dependency: "direct main"
description:
name: photo_view
sha256: "8036802a00bae2a78fc197af8a158e3e2f7b500561ed23b4c458107685e645bb"
url: "https://pub.dev"
source: hosted
version: "0.14.0"
platform:
dependency: transitive
description:

View File

@ -58,6 +58,8 @@ dependencies:
path_provider: ^2.1.2
flutter_typeahead: ^5.2.0
decimal: ^2.3.3
photo_view: ^0.14.0
image_gallery_saver: ^2.0.3
dev_dependencies: