feat: inventory inout TIME

This commit is contained in:
louis 2024-04-10 17:08:26 +08:00
parent 322368339f
commit 11df20c98e
20 changed files with 398 additions and 75 deletions

View File

@ -15,6 +15,11 @@ class Api {
);
}
///
static Future<Response> getResources() {
return DioService.dio.get(Urls.accountResources);
}
///
static Future<Response<PaginationData>> getRoles(Map params) {
return DioService.dio.get<PaginationData>(Urls.roles,

View File

@ -27,7 +27,7 @@ class AppTheme {
static const Color dividerColor = Color.fromARGB(255, 224, 224, 224);
static const Color appbarBgColor = AppTheme.primaryColor;
static const Color scaffoldBackgroundColor = Color(0XFFe9f0fd);
static const Color inputFillColor = Color(0xFFf5f8ff);
static const Color inputFillColor = Color(0xfffefefe);
}
final theme = ThemeData(
@ -48,17 +48,29 @@ final theme = ThemeData(
fontFamily: AppTheme.fontName,
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
textStyle: MaterialStateProperty.all<TextStyle>(TextStyle(
fontSize: ScreenAdaper.height(26),
)), //
foregroundColor:
MaterialStateProperty.all<Color>(AppTheme.primaryColor), //
)),
floatingActionButtonTheme: FloatingActionButtonThemeData(
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: AppTheme.primaryColorLight,
),
dialogTheme: const DialogTheme(
elevation: 0.2,
backgroundColor: AppTheme.nearlyWhite,
),
dialogBackgroundColor: AppTheme.nearlyWhite,
colorScheme: ColorScheme.fromSeed(
onPrimary: AppTheme.nearlyWhite,
seedColor: AppTheme.primaryColor,
primary: AppTheme.primaryColor),
datePickerTheme: DatePickerThemeData(
confirmButtonStyle: ButtonStyle(
textStyle: MaterialStateProperty.resolveWith<TextStyle?>(
(Set<MaterialState> states) {
return const TextStyle(color: AppTheme.primaryColor);
return TextStyle(color: AppTheme.primaryColor);
},
),
),
@ -84,6 +96,7 @@ final theme = ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
primaryColor: AppTheme.primaryColor,
primaryColorDark: AppTheme.primaryColorDark,
textTheme: TextTheme(bodySmall: TextStyle(fontSize: ScreenAdaper.height(26))),
progressIndicatorTheme:
const ProgressIndicatorThemeData(color: AppTheme.primaryColor),
dividerColor: AppTheme.dividerColor,

View File

@ -14,7 +14,7 @@ class Urls {
static String getDictType = 'system/dict-type/all';
static String uploadAttachemnt = 'tools/upload';
static String systemParamConfig = 'system/param-config';
static String accountMenus = 'account/menus';
static String accountResources = 'account/menus';
static String depts = 'system/depts';
static String roles = 'system/roles';
}

View File

@ -14,8 +14,8 @@ class RouteConfig {
static const String login = '/login';
static const String userinfo = '/userinfo';
static const String inventory = '/inventory';
static const String saleQuotation = '/sale_quotation';
static const String hrManage = '/hr_manage';
static const String saleQuotation = '/workbench/sale_quotation';
static const String hrManage = '/workbench/hr_manage';
static const String employeeDetail = '/employee_detail';
static final List<GetPage> getPages = [

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/store/dict.store.dart';
import 'package:sk_base_mobile/store/resource.store.dart';
import 'package:sk_base_mobile/util/loading_util.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'package:sk_base_mobile/constants/cache_key.dart';
@ -48,6 +49,9 @@ class Global {
///
await Get.putAsync<DictService>(() => DictService().init());
///
await Get.putAsync<ResourceService>(() => ResourceService().init());
if (StorageService.to.getString(CacheKeys.token, isWithUser: false) !=
null) {
///

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/config.dart';
@ -31,7 +32,16 @@ class IndexPage extends StatelessWidget {
maxOverScrollExtent: 80,
footerTriggerDistance: 150,
child: GetMaterialApp(
theme: theme,
locale: const Locale('zh', 'CN'), //
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate, //
],
supportedLocales: const [
Locale('zh', 'CN'), //
],
theme: _getAppTheme(),
title: GloablConfig.DOMAIN_NAME,
getPages: RouteConfig.getPages,
initialRoute: getInitialRoute(),
@ -55,4 +65,8 @@ class IndexPage extends StatelessWidget {
bool isLogined = token != null;
return isLogined ? RouteConfig.home : RouteConfig.login;
}
ThemeData _getAppTheme() {
return theme;
}
}

View File

@ -0,0 +1,98 @@
class ResourceModel {
ResourceModel({
required this.id,
required this.path,
required this.component,
required this.name,
required this.meta,
required this.redirect,
required this.children,
});
final int? id;
final String? path;
final String? component;
final String? name;
final Meta? meta;
final String? redirect;
final List<ResourceModel> children;
factory ResourceModel.fromJson(Map<String, dynamic> json) {
return ResourceModel(
id: json["id"],
path: json["path"],
component: json["component"],
name: json["name"],
meta: json["meta"] == null ? null : Meta.fromJson(json["meta"]),
redirect: json["redirect"],
children: json["children"] == null
? []
: List<ResourceModel>.from(
json["children"]!.map((x) => ResourceModel.fromJson(x))),
);
}
Map<String, dynamic> toJson() => {
"id": id,
"path": path,
"component": component,
"name": name,
"meta": meta?.toJson(),
"redirect": redirect,
"children": children.map((x) => x?.toJson()).toList(),
};
}
class Meta {
Meta({
required this.title,
required this.icon,
required this.isExt,
required this.extOpenMode,
required this.type,
required this.orderNo,
required this.show,
required this.activeMenu,
required this.status,
required this.keepAlive,
});
final String? title;
final String? icon;
final bool? isExt;
final int? extOpenMode;
final int? type;
final int? orderNo;
final int? show;
final String? activeMenu;
final int? status;
final int? keepAlive;
factory Meta.fromJson(Map<String, dynamic> json) {
return Meta(
title: json["title"],
icon: json["icon"],
isExt: json["isExt"],
extOpenMode: json["extOpenMode"],
type: json["type"],
orderNo: json["orderNo"],
show: json["show"],
activeMenu: json["activeMenu"],
status: json["status"],
keepAlive: json["keepAlive"],
);
}
Map<String, dynamic> toJson() => {
"title": title,
"icon": icon,
"isExt": isExt,
"extOpenMode": extOpenMode,
"type": type,
"orderNo": orderNo,
"show": show,
"activeMenu": activeMenu,
"status": status,
"keepAlive": keepAlive,
};
}

View File

@ -194,7 +194,7 @@ class EditUserInfo extends StatelessWidget {
image: controller.filePath.value.isNotEmpty
? FileImage(File(controller.filePath.value))
: NetworkImage(MediaUtil.getMediaUrl(
controller.userInfo.value!.avatar!))
controller.userInfo.value.avatar))
as ImageProvider<Object>),
)),
),

View File

@ -7,6 +7,7 @@ import 'package:sk_base_mobile/constants/bg_color.dart';
import 'package:sk_base_mobile/models/user_info.model.dart';
import 'package:sk_base_mobile/screens/inventory_inout/components/responsive.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/core/sk_avatar.dart';
import 'package:sk_base_mobile/widgets/core/sk_tag.dart';
import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
@ -44,13 +45,7 @@ class EmployeeDetail extends StatelessWidget {
color: AppTheme.white,
child: Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(30)),
child: FadeInCacheImage(
height: ScreenAdaper.height(80),
width: ScreenAdaper.height(80),
url: '${GloablConfig.OSS_URL}${userInfo.avatar}'),
),
SkAvatar(url: userInfo.avatar),
SizedBox(
width: ScreenAdaper.width(defaultPadding),
),

View File

@ -13,8 +13,10 @@ import 'package:sk_base_mobile/util/debouncer.dart';
import 'package:sk_base_mobile/util/device.util.dart';
import 'package:sk_base_mobile/util/media_util.dart';
import 'package:sk_base_mobile/util/modal.util.dart';
import 'package:sk_base_mobile/util/router.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/util/snack_bar.util.dart';
import 'package:sk_base_mobile/widgets/core/sk_avatar.dart';
import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
import 'package:sk_base_mobile/widgets/core/sk_tag.dart';
import 'package:sk_base_mobile/widgets/form_item/sk_text_input.dart';
@ -149,7 +151,7 @@ class HrManagePage extends StatelessWidget {
Widget buildUserCard(int index) {
return SkInk(
onTap: () {
Get.toNamed(RouteConfig.employeeDetail,
RouterUtil.toNamed(RouteConfig.employeeDetail,
arguments: controller.list[index]);
},
margin: EdgeInsets.only(bottom: ScreenAdaper.height(defaultPadding)),
@ -163,14 +165,7 @@ class HrManagePage extends StatelessWidget {
Row(
children: [
//
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(30)),
child: FadeInCacheImage(
height: ScreenAdaper.height(80),
width: ScreenAdaper.height(80),
url:
MediaUtil.getMediaUrl(controller.list[index].avatar)),
),
SkAvatar(url: controller.list[index].avatar),
SizedBox(
width: ScreenAdaper.height(defaultPadding),
),

View File

@ -13,6 +13,7 @@ import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_
import 'package:sk_base_mobile/screens/new_inventory_inout/components/product_search.dart';
import 'package:sk_base_mobile/store/dict.store.dart';
import 'package:sk_base_mobile/util/util.dart';
import 'package:sk_base_mobile/widgets/form_item/sk_datetime_picker.dart';
import 'package:sk_base_mobile/widgets/form_item/sk_number_input.dart';
import 'package:sk_base_mobile/widgets/form_item/sk_search_select.dart';
import 'package:sk_base_mobile/widgets/form_item/sk_date_picker.dart';
@ -75,28 +76,35 @@ class NewInventoryInout extends StatelessWidget {
SizedBox(
height: ScreenAdaper.height(formVerticalGap),
),
if (inOrOut == InventoryInOrOutEnum.In) ...[
Row(
children: [
if (inOrOut == InventoryInOrOutEnum.In) ...[
Expanded(flex: 1, child: buildUnitPrice()),
SizedBox(
width: ScreenAdaper.width(formHorizontalGap),
),
Expanded(flex: 1, child: buildAmount())
],
),
SizedBox(
height: ScreenAdaper.height(formVerticalGap),
),
],
if (inOrOut == InventoryInOrOutEnum.In) ...[
Row(
children: [
Expanded(child: buildDatePicker()),
SizedBox(
width: ScreenAdaper.width(formHorizontalGap),
),
Expanded(child: buildAgent()),
],
),
SizedBox(
height: ScreenAdaper.height(formVerticalGap),
),
// buildDatePicker(),
// SizedBox(
// height: ScreenAdaper.height(formVerticalGap),
// ),
buildAgent(),
SizedBox(
height: ScreenAdaper.height(formVerticalGap),
),
],
if (inOrOut == InventoryInOrOutEnum.Out) buildAgent(),
if (inOrOut == InventoryInOrOutEnum.In) buildPositionBottomPicker(),
SizedBox(
height: ScreenAdaper.height(formVerticalGap),
@ -281,15 +289,16 @@ class NewInventoryInout extends StatelessWidget {
///
Widget buildDatePicker() {
return SkDatePicker(
return SkDateTimePicker(
initialDate: DateTime.tryParse('${controller.payload['time'] ?? ''}'),
textController: controller.dateTextController,
onClear: () {
controller.payload.remove('time');
},
onDateSelected: (date) {
onSelected: (date) {
if (date != null) {
controller.dateTextController.text = SkDateUtil.format(date);
controller.payload['time'] = controller.dateTextController.text;
controller.payload['time'] = date.millisecondsSinceEpoch;
}
},
);

View File

@ -75,7 +75,7 @@ class NewInventoryInoutController extends GetxController {
onReady() {
super.onReady();
dateTextController.text = SkDateUtil.format(DateTime.now());
payload['time'] = SkDateUtil.format(DateTime.now());
payload['time'] = DateTime.now().microsecondsSinceEpoch;
payload['inOrOut'] = inOrOut;
}
@ -128,29 +128,8 @@ class NewInventoryInoutController extends GetxController {
);
return;
}
//
// if (payload['time'] == null) {
// SnackBarUtil().error(
// '时间不能为空',
// );
// return;
// }
payload['time'] = SkDateUtil.format(DateTime.now(), formats: [
'yyyy',
'-',
'mm',
'-',
'dd',
' ',
'HH',
':',
'nn',
":",
'ss'
]);
payload['quantity'] = quantityTextController.text;
if (payload['quantity'].isEmpty || payload['quantity'] == '0') {
SnackBarUtil().error(
'数量必须大于1',
@ -186,11 +165,10 @@ class NewInventoryInoutController extends GetxController {
} else {
payload['amount'] = amountTextController.text;
}
payload['time'] = payload['time'] ?? DateTime.now().microsecondsSinceEpoch;
payload['remark'] = remarkTextController.text;
await LoadingUtil.to.show(status: '提交中请稍后...');
// uploadImgFilesPath
try {
// final recordId = res.data as int;
//

View File

@ -1,4 +1,5 @@
import 'package:get/get.dart';
import 'package:sk_base_mobile/constants/constants.dart';
import 'package:sk_base_mobile/models/workbench.model.dart';
class WorkBenchController extends GetxController {
@ -6,11 +7,13 @@ class WorkBenchController extends GetxController {
WorkBenchModel(title: '库存', route: '/inventory', icon: 'inventory.svg'),
WorkBenchModel(title: '产品', route: '/product', icon: 'product.svg'),
WorkBenchModel(title: '合同', route: '/contract', icon: 'contract.svg'),
WorkBenchModel(title: '人事', route: '/hr_manage', icon: 'hr.svg'),
WorkBenchModel(title: '人事', route: RouteConfig.hrManage, icon: 'hr.svg'),
WorkBenchModel(title: '公车', route: '/vehicle', icon: 'vehicle.svg'),
WorkBenchModel(title: '任务', route: '/task_manage', icon: 'task_manage.svg'),
WorkBenchModel(title: '报表', route: '/report', icon: 'report.svg'),
WorkBenchModel(
title: '报价计算', route: '/sale_quotation', icon: 'sale_quotation.svg'),
title: '报价计算',
route: RouteConfig.saleQuotation,
icon: 'sale_quotation.svg'),
];
}

View File

@ -0,0 +1,32 @@
import 'package:get/get.dart';
import 'package:sk_base_mobile/apis/api.dart';
import 'package:sk_base_mobile/models/resource.model.dart';
import 'package:sk_base_mobile/store/auth.store.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
class ResourceService extends GetxService {
static ResourceService get to => Get.find();
Future<ResourceService> init() async {
if (AuthStore.to.userInfo.value.id == null) return this;
await getResources();
return this;
}
RxList<ResourceModel> resources = RxList([]);
Future<void> getResources() async {
try {
final response = await Api.getResources();
resources.value = (response.data as List)
.map((item) => ResourceModel.fromJson(item))
.toList();
} catch (e) {
LoggerUtil().error('getResources error: $e');
}
}
// List<DictItemModel> getDictItemsByCode(String code) {
// return dictTypes.firstWhereOrNull((item) => item.code == code)?.dictItems ??
// [];
// }
}

12
lib/util/router.util.dart Normal file
View File

@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class RouterUtil {
static Future<T?> toNamed<T>(String routeName, {arguments}) async {
//
if (Get.context != null) {
FocusScope.of(Get.context!).requestFocus(FocusNode());
}
return await Get.toNamed<T?>(routeName, arguments: arguments);
}
}

View File

@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/media_util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
class SkAvatar extends StatelessWidget {
final String? url;
const SkAvatar({super.key, this.url});
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(40)),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: AppTheme.dividerColor),
borderRadius: const BorderRadius.all(Radius.circular(40))),
child: FadeInCacheImage(
defaultWidget: Icon(Icons.person_2_outlined,
size: ScreenAdaper.height(60), color: AppTheme.grey),
height: ScreenAdaper.height(80),
width: ScreenAdaper.height(80),
url: MediaUtil.getMediaUrl(url)),
),
);
}
}

View File

@ -11,11 +11,13 @@ class FadeInCacheImage extends StatefulWidget {
final BoxFit? fit;
final bool compress;
final bool canFullscreen;
final Widget? defaultWidget;
const FadeInCacheImage(
{super.key,
this.width,
this.height,
this.url,
this.defaultWidget,
this.fit = BoxFit.cover,
this.compress = false,
this.canFullscreen = false});
@ -78,7 +80,8 @@ class _FadeInCacheImageState extends State<FadeInCacheImage> {
alignment: Alignment.center,
width: widget.width,
height: widget.height,
child: Icon(Icons.image_not_supported,
child: widget.defaultWidget ??
Icon(Icons.image_not_supported,
size: ScreenAdaper.height((widget.width ?? 200)),
color: AppTheme.grey),
);

View File

@ -0,0 +1,114 @@
import 'package:flutter/material.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_datetime_picker/sk_datetime_picker.dart';
class SkDateTimePicker extends StatelessWidget {
final TextEditingController textController;
final VoidCallback? onClear;
final Function(DateTime?)? onSelected;
final bool isRequired;
final String labelText;
final DateTime? initialDate;
const SkDateTimePicker({
super.key,
this.onClear,
this.onSelected,
this.initialDate,
this.labelText = '日期',
this.isRequired = false,
required this.textController,
});
@override
Widget build(BuildContext context) {
return TextFormField(
controller: textController,
onTapOutside: (event) {
FocusScope.of(context).unfocus();
},
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.always,
prefixIcon: Icon(
Icons.date_range_outlined,
size: ScreenAdaper.height(40),
),
suffixIcon: textController.text.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear),
// TextFormField的值
onPressed: () {
textController.clear();
if (onClear != null) {
onClear!();
}
},
)
: const SizedBox(),
hintText: '请选择',
label: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
if (isRequired)
Text(
"*",
style: TextStyle(
color: Colors.red, fontSize: ScreenAdaper.height(30)),
),
Text(
labelText,
style: TextStyle(fontSize: ScreenAdaper.height(30)),
),
])),
keyboardType: TextInputType.none,
onTap: () async {
DateTime? dateTime = await showSkDateTimePicker(
theme: theme,
is24HourMode: true,
title: Container(
padding: EdgeInsets.symmetric(vertical: ScreenAdaper.height(10)),
child: Text(
'选择日期时间',
style: TextStyle(fontSize: ScreenAdaper.height(30)),
),
),
context: context,
initialDate: initialDate ?? DateTime.now(),
firstDate: DateTime(1600).subtract(const Duration(days: 3652)),
lastDate: DateTime.now().add(
const Duration(days: 3652),
),
isForce2Digits: true,
isShowSeconds: false,
minutesInterval: 1,
secondsInterval: 1,
borderRadius: const BorderRadius.all(Radius.circular(16)),
constraints: const BoxConstraints(
maxWidth: 350,
maxHeight: 650,
),
transitionBuilder: (context, anim1, anim2, child) {
return FadeTransition(
opacity: anim1.drive(
Tween(
begin: 0,
end: 1,
),
),
child: child,
);
},
transitionDuration: const Duration(milliseconds: 200),
barrierDismissible: true,
selectableDayPredicate: (dateTime) {
return true;
},
);
if (onSelected != null) {
onSelected!(dateTime);
}
},
);
}
}

View File

@ -334,6 +334,11 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.2"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_native_splash:
dependency: "direct main"
description:
@ -532,10 +537,10 @@ packages:
dependency: transitive
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
url: "https://pub.dev"
source: hosted
version: "0.19.0"
version: "0.18.1"
js:
dependency: transitive
description:
@ -896,6 +901,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
sk_datetime_picker:
dependency: "direct main"
description:
path: "."
ref: HEAD
resolved-ref: "2a7073a2e7fee1ddf0ba67ce87ab2716de876e25"
url: "https://gitee.com/lu-zixun/sk-date-time-picker.git"
source: git
version: "1.0.9"
sky_engine:
dependency: transitive
description: flutter

View File

@ -30,7 +30,8 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
@ -68,6 +69,11 @@ dependencies:
math_expressions: ^2.4.0
install_plugin: ^2.1.0
url_launcher: ^6.2.5
sk_datetime_picker:
git:
url: https://gitee.com/lu-zixun/sk-date-time-picker.git
# sk_datetime_picker:
# path: ./lib/widgets/common/datetime_picker
dev_dependencies:
flutter_test:
sdk: flutter