feat: upgrade apk.

This commit is contained in:
louis 2024-04-07 17:32:46 +08:00
parent 10145b3af7
commit 1befdcb786
40 changed files with 719 additions and 229 deletions

View File

@ -43,6 +43,7 @@
android:name="android.permission.CAMERA" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
assets/images/rocket.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 KiB

BIN
assets/images/rocket.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -7,7 +7,7 @@ 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,
@ -15,6 +15,13 @@ class Api {
);
}
/// By key
static Future<Response> getSystemParamConfigByCode(String code) {
return DioService.dio.get(
'${Urls.systemParamConfig}/key/$code',
);
}
//
static Future<Response> getUserInfo() {
return DioService.dio.get(

View File

@ -28,6 +28,7 @@ class AppTheme {
}
final theme = ThemeData(
platform: TargetPlatform.iOS,
primarySwatch: MaterialColor(AppTheme.primaryColor.value, const {
50: AppTheme.primaryColorLight,
100: AppTheme.primaryColorLight,

View File

@ -2,11 +2,11 @@
// Global config
class GloablConfig {
static const BASE_URL = "http://10.0.2.2:8001/api/";
static const OSS_URL = "http://10.0.2.2:8001";
// static const BASE_URL = "http://10.0.2.2:8001/api/";
// static const OSS_URL = "http://10.0.2.2:8001";
// static const BASE_URL = "http://144.123.43.138:3001/api/";
// static const OSS_URL = "http://144.123.43.138:3001";
static const BASE_URL = "http://144.123.43.138:3001/api/";
static const OSS_URL = "http://144.123.43.138:3001";
// static const BASE_URL = "http://192.168.60.220:8001/api/";
// static const OSS_URL = "http://192.168.60.220:8001";
static const DOMAIN_NAME = "山矿通";

View File

@ -6,3 +6,4 @@ export 'global_url.dart';
export 'cache_key.dart';
export 'router.dart';
export 'text_enum.dart';
export 'system_param_config.dart';

View File

@ -13,4 +13,6 @@ class Urls {
static String updateAvatar = 'user/updateAvatar';
static String getDictType = 'system/dict-type/all';
static String uploadAttachemnt = 'tools/upload';
static String systemParamConfig = 'system/param-config';
static String accountMenus = 'account/menus';
}

View File

@ -0,0 +1,4 @@
class SystemParamConfig {
static const String appVersion = "app_version";
static const String isForceUpgrade = "is_app_force_upgrade";
}

View File

@ -8,7 +8,7 @@ Future<void> main() async {
try {
await Global.init();
} catch (e) {
LoggerUtil().error('Init failed, please try again.$e');
LoggerUtil().error('Init failed, please try again. $e');
}
// await SentryFlutter.init(
// (options) {

View File

@ -7,7 +7,7 @@ class InventoryInOutModel {
required this.id,
required this.createdAt,
required this.updatedAt,
required this.inventoryNumber,
required this.inventoryInOutNumber,
required this.productId,
required this.inOrOut,
required this.time,
@ -28,7 +28,7 @@ class InventoryInOutModel {
final int? id;
final DateTime? createdAt;
final DateTime? updatedAt;
final String? inventoryNumber;
final String? inventoryInOutNumber;
final int? productId;
final int? inOrOut;
final DateTime? time;
@ -47,12 +47,12 @@ class InventoryInOutModel {
factory InventoryInOutModel.fromJson(Map<String, dynamic> json) {
return InventoryInOutModel(
id: json["id"],
createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
inventoryNumber: json["inventoryNumber"],
createdAt: DateTime.tryParse(json["createdAt"] ?? "")?.toLocal(),
updatedAt: DateTime.tryParse(json["updatedAt"] ?? "")?.toLocal(),
inventoryInOutNumber: json["inventoryInOutNumber"],
productId: json["productId"],
inOrOut: json["inOrOut"],
time: DateTime.tryParse(json["time"] ?? ""),
time: DateTime.tryParse(json["time"] ?? "")?.toLocal(),
quantity: json["quantity"],
unitPrice: json["unitPrice"],
amount: json["amount"],
@ -81,7 +81,7 @@ class InventoryInOutModel {
"id": id,
"createdAt": createdAt?.toIso8601String(),
"updatedAt": updatedAt?.toIso8601String(),
"inventoryNumber": inventoryNumber,
"inventoryInOutNumber": inventoryInOutNumber,
"productId": productId,
"inOrOut": inOrOut,
"time": time,

View File

@ -3,12 +3,13 @@ import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_
import 'package:sk_base_mobile/widgets/sk_appbar.dart';
class InventoryPage extends StatelessWidget {
const InventoryPage({super.key});
final bool isPage;
const InventoryPage({super.key, this.isPage = false});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const SkAppbar(title: '库存管理'),
appBar: SkAppbar(title: '库存管理', hideLeading: isPage),
body: InventorySearch(),
);
}

View File

@ -109,7 +109,11 @@ class InventoryInoutCard extends StatelessWidget {
),
),
Text(
controller.list[ind][index].agent ?? '-',
'${DateUtil.format(controller.list[ind][index].time!, formats: [
'HH',
':',
"nn"
])} ${controller.list[ind][index].agent ?? '-'}',
style: TextStyle(
fontSize: ScreenAdaper.height(20),
fontWeight: FontWeight.w600),

View File

@ -61,9 +61,21 @@ class InventoryInoutInfo extends StatelessWidget {
customDivider(),
],
buildListItem(
leading: '出入库时间',
trailing: DateUtil.format(
controller.inventoryInoutInfo.value!.time!)),
leading: '出入库时间',
trailing: DateUtil.format(
controller.inventoryInoutInfo.value!.time!,
formats: [
'yyyy',
'-',
'mm',
'-',
'dd',
" ",
'HH',
":",
"nn"
]),
),
customDivider(),
buildListItem(
leading: '所属公司',
@ -99,6 +111,16 @@ class InventoryInoutInfo extends StatelessWidget {
trailing:
controller.inventoryInoutInfo.value?.remark),
customDivider(),
buildListItem(
leading: '出入库单号',
trailing: controller
.inventoryInoutInfo.value?.inventoryInOutNumber),
customDivider(),
buildListItem(
leading: '库存编号',
trailing: controller.inventoryInoutInfo.value
?.inventory?.inventoryNumber),
customDivider(),
buildListItem(
leading: '照片',
),
@ -156,14 +178,31 @@ class InventoryInoutInfo extends StatelessWidget {
}
Widget buildListItem({String? leading, dynamic trailing}) {
return ListTile(
leading: Text(
'${leading ?? ''}: ',
style: TextStyle(
fontSize: ScreenAdaper.height(25), fontWeight: FontWeight.w600),
),
trailing: Text('${trailing ?? ''}',
style: TextStyle(fontSize: ScreenAdaper.height(25))));
return Container(
padding: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(20),
horizontal: ScreenAdaper.width(20)),
child: Row(
children: [
Text(
'${leading ?? ''}: ',
style: TextStyle(
fontSize: ScreenAdaper.height(30), fontWeight: FontWeight.w600),
),
Spacer(),
Text('${trailing ?? ''}',
style: TextStyle(fontSize: ScreenAdaper.height(30)))
],
),
);
// return ListTile(
// leading: Text(
// '${leading ?? ''}: ',
// style: TextStyle(
// fontSize: ScreenAdaper.height(25), fontWeight: FontWeight.w600),
// ),
// trailing: Text('${trailing ?? ''}',
// style: TextStyle(fontSize: ScreenAdaper.height(25))));
}
}

View File

@ -6,7 +6,9 @@ import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/constants/enum.dart';
import 'package:sk_base_mobile/db_helper/db_help.dart';
import 'package:sk_base_mobile/screens/inventory_inout/components/inventory_inout_info.dart';
import 'package:sk_base_mobile/screens/landing/landing_controller.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout.dart';
import 'package:sk_base_mobile/services/app_info.service.dart';
import 'package:sk_base_mobile/util/date.util.dart';
import 'package:sk_base_mobile/util/modal.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
@ -236,7 +238,7 @@ class InventoryInoutController extends GetxController {
Future showInventoryInoutInfoDialog(int id) async {
ModalUtil.showGeneralDialog(
width: ScreenAdaper.screenShortDistance() - ScreenAdaper.width(100),
height: ScreenAdaper.height(Get.height - 100),
height: Get.height - 100 < 400 ? 400 : Get.height - 100,
content: InventoryInoutInfo(inventoryInoutId: id),
offset: const Offset(0, -1))
.then((value) => {Get.delete<InventoryInouInfoController>()});
@ -258,6 +260,7 @@ class InventoryInoutController extends GetxController {
}
return [];
} catch (e) {
print(e);
return [];
} finally {
loading.value = false;
@ -335,7 +338,7 @@ class InventoryInoutController extends GetxController {
// }
// case 2:
// {
// ModalUtil.confirm(
// ModalUtil.alert(
// 'Delete Task', 'Are you want to sure to remove', 'Confirm', () {
// list[ind].remove(list[ind][index]);
// db.delete(

View File

@ -1,10 +1,16 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/models/app_bottom_nav_item.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/screens/mine/mine.dart';
import 'package:sk_base_mobile/screens/workbench/workbench.dart';
import 'package:sk_base_mobile/services/app_info.service.dart';
import 'package:sk_base_mobile/util/device.util.dart';
import 'package:sk_base_mobile/util/modal.util.dart';
import '../../constants/constants.dart';
class LandingController extends GetxController {
RxInt currentIndex = 0.obs;
@ -20,7 +26,7 @@ class LandingController extends GetxController {
icon: Icons.inventory_outlined,
activeIcon: Icons.inventory_rounded,
label: '库存',
page: const InventoryPage()),
page: const InventoryPage(isPage: true)),
AppBottomNavItem(
icon: Icons.widgets_outlined,
activeIcon: Icons.widgets_rounded,
@ -36,5 +42,6 @@ class LandingController extends GetxController {
onInit() {
super.onInit();
pages = bottomNavItems!.map((e) => e.page!).toList();
AppInfoService.to.checkVersion();
}
}

View File

@ -1,6 +1,9 @@
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/util/device.util.dart';
import 'package:sk_base_mobile/util/snack_bar.util.dart';
import '../../constants/constants.dart';
import '../../store/auth.store.dart';
// import 'package:sentry/sentry.dart';

View File

@ -35,11 +35,14 @@ class _MinePageState extends State<MinePage>
Widget _buildBody() {
return Column(children: [
Container(
height: ScreenAdaper.height(300),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage('assets/images/yeyazhijia_bg.jpg'))),
height: ScreenAdaper.height(350),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [AppTheme.primaryColorLight, AppTheme.primaryColor])
// image: DecorationImage(
// fit: BoxFit.cover,
// image: AssetImage('assets/images/yeyazhijia_bg.jpg'))
),
// decoration: BoxDecoration(
// gradient: LinearGradient(
// colors: [AppTheme.primaryColorLight, AppTheme.primaryColor])),
@ -49,7 +52,7 @@ class _MinePageState extends State<MinePage>
vertical: ScreenAdaper.height(20)),
child: Column(children: [
SizedBox(
height: ScreenAdaper.height(80),
height: ScreenAdaper.height(100),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
@ -69,7 +72,7 @@ class _MinePageState extends State<MinePage>
AuthStore.to.userInfo.value.nickname ?? '',
style: TextStyle(
letterSpacing: ScreenAdaper.width(5),
fontSize: ScreenAdaper.height(30),
fontSize: ScreenAdaper.height(35),
fontWeight: FontWeight.bold,
foreground: Paint()
..style = PaintingStyle.stroke
@ -81,7 +84,7 @@ class _MinePageState extends State<MinePage>
style: TextStyle(
letterSpacing: ScreenAdaper.width(5),
color: Colors.white,
fontSize: ScreenAdaper.height(30),
fontSize: ScreenAdaper.height(35),
fontWeight: FontWeight.bold),
),
],

View File

@ -1,6 +1,9 @@
import 'package:flutter/material.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/constants/bg_color.dart';
import 'package:sk_base_mobile/services/app_info.service.dart';
import 'package:sk_base_mobile/store/auth.store.dart';
import 'package:sk_base_mobile/util/device.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class MineSettingsPage extends StatelessWidget {
@ -18,7 +21,7 @@ class MineSettingsPage extends StatelessWidget {
border: Border.all(),
borderRadius: BorderRadius.circular(15),
color: AppTheme.nearlyWhite),
width: ScreenAdaper.width(400),
width: ScreenAdaper.width(600),
padding:
EdgeInsets.symmetric(vertical: ScreenAdaper.width(10)),
child: InkWell(
@ -35,7 +38,44 @@ class MineSettingsPage extends StatelessWidget {
Text(
'退出登录',
style: TextStyle(
fontSize: ScreenAdaper.height(20),
fontSize: ScreenAdaper.height(30),
fontWeight: FontWeight.w600),
),
],
)))),
SizedBox(
height: ScreenAdaper.height(defaultPadding),
),
Container(
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.circular(15),
color: AppTheme.nearlyWhite),
width: ScreenAdaper.width(600),
padding:
EdgeInsets.symmetric(vertical: ScreenAdaper.width(10)),
child: InkWell(
onTap: () async {
await AppInfoService.to.checkVersion();
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.width(20),
vertical: ScreenAdaper.width(20)),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
'v${AppInfoService.to.versionNumber}',
style: TextStyle(
fontSize: ScreenAdaper.height(25),
fontWeight: FontWeight.w400),
),
const Spacer(),
Text(
'检查更新',
style: TextStyle(
fontSize: ScreenAdaper.height(30),
fontWeight: FontWeight.w600),
),
],

View File

@ -84,10 +84,10 @@ class NewInventoryInout extends StatelessWidget {
SizedBox(
height: ScreenAdaper.height(formVerticalGap),
),
buildDatePicker(),
SizedBox(
height: ScreenAdaper.height(formVerticalGap),
),
// buildDatePicker(),
// SizedBox(
// height: ScreenAdaper.height(formVerticalGap),
// ),
buildAgent(),
SizedBox(
height: ScreenAdaper.height(formVerticalGap),

View File

@ -128,13 +128,29 @@ class NewInventoryInoutController extends GetxController {
);
return;
}
if (payload['time'] == null) {
SnackBarUtil().error(
'时间不能为空',
);
return;
}
//
// if (payload['time'] == null) {
// SnackBarUtil().error(
// '时间不能为空',
// );
// return;
// }
payload['time'] = DateUtil.format(DateTime.now(), formats: [
'yyyy',
'-',
'mm',
'-',
'dd',
' ',
'HH',
':',
'nn',
":",
'ss'
]);
payload['quantity'] = quantityTextController.text;
if (payload['quantity'].isEmpty || payload['quantity'] == '0') {
SnackBarUtil().error(
'数量必须大于1',

View File

@ -8,9 +8,7 @@ import 'package:sk_base_mobile/models/base_search_more_controller.dart';
import 'package:sk_base_mobile/models/sale_quotation.model.dart';
import 'package:sk_base_mobile/screens/sale_quotation/components/sale_quotation_group_search.dart';
import 'package:sk_base_mobile/services/storage.service.dart';
import 'package:sk_base_mobile/util/snack_bar.util.dart';
import 'package:sk_base_mobile/widgets/core/sk_muti_search_more.dart';
import 'package:sk_base_mobile/widgets/core/sk_single_search_more.dart';
import 'package:sk_base_mobile/util/modal.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:pinyin/pinyin.dart';
@ -20,19 +18,25 @@ class SaleQuotationController extends GetxController {
final RxList editingcell = RxList([null, null, null]);
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
RxList<SaleQuotationItemModel> products = RxList([
SaleQuotationItemModel(name: '矿用本安型支架控制器', unit: '', spec: 'ZDYZ-Z'),
SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器'),
SaleQuotationItemModel(name: '矿用隔爆兼本安型电源'),
SaleQuotationItemModel(name: '矿用本安型隔离耦合器'),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器'),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器'),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器'),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器'),
SaleQuotationItemModel(name: '矿用本安型支架无线遥控器'),
SaleQuotationItemModel(name: '矿用隔爆兼本安型电源'),
SaleQuotationItemModel(name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组'),
SaleQuotationItemModel(name: '电液换向阀(20功能20接口)', remark: '端头架主阀组'),
SaleQuotationItemModel(name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm'),
SaleQuotationItemModel(
name: '矿用本安型支架控制器', unit: '', spec: 'ZDYZ-Z', cost: 4700),
SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
SaleQuotationItemModel(name: '矿用本安型隔离耦合器', cost: 1200),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', cost: 600),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器', cost: 400),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器', cost: 500),
SaleQuotationItemModel(
name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器', cost: 2000),
SaleQuotationItemModel(name: '矿用本安型支架控制器', cost: 4700),
SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
SaleQuotationItemModel(
name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组', cost: 13200),
SaleQuotationItemModel(
name: '电液换向阀(20功能20接口)', remark: '端头架主阀组', cost: 26500),
SaleQuotationItemModel(
name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm', cost: 2000),
SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'),
SaleQuotationItemModel(name: '矿用本安型位移传感器'),
SaleQuotationItemModel(name: '矿用本安型压力传感器'),
@ -187,18 +191,54 @@ class SaleQuotationController extends GetxController {
},
leadingBuilder: (index) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.width(5),
vertical: ScreenAdaper.height(10)),
child: Row(
children: [
Text(
controller.list[index].name,
style: TextStyle(fontSize: ScreenAdaper.height(25)),
),
],
),
);
padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.width(5),
vertical: ScreenAdaper.height(10)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
controller.list[index].name,
style: TextStyle(
fontSize: ScreenAdaper.height(30)),
),
],
),
if (controller.list[index].spec != null)
Row(
children: [
Text(
'型号:${controller.list[index].spec ?? ''}',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: ScreenAdaper.height(25)),
),
],
),
Row(
children: [
Text(
'单价:¥${controller.list[index].cost}',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: ScreenAdaper.height(25)),
),
],
),
if (controller.list[index].remark != null)
Row(
children: [
Text(
'备注:${controller.list[index].remark ?? ''}',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: ScreenAdaper.height(25)),
),
],
),
]));
},
),
width: Get.width - ScreenAdaper.width(50))
@ -239,19 +279,25 @@ class ItemSearchMoreController extends GetxController
@override
Future<List<SaleQuotationItemModel>> getData({bool isRefresh = false}) async {
List<SaleQuotationItemModel> newList = [
SaleQuotationItemModel(name: '矿用本安型支架控制器', unit: '', spec: 'ZDYZ-Z'),
SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器'),
SaleQuotationItemModel(name: '矿用隔爆兼本安型电源'),
SaleQuotationItemModel(name: '矿用本安型隔离耦合器'),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器'),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器'),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器'),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器'),
SaleQuotationItemModel(name: '矿用本安型支架无线遥控器'),
SaleQuotationItemModel(name: '矿用隔爆兼本安型电源'),
SaleQuotationItemModel(name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组'),
SaleQuotationItemModel(name: '电液换向阀(20功能20接口)', remark: '端头架主阀组'),
SaleQuotationItemModel(name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm'),
SaleQuotationItemModel(
name: '矿用本安型支架控制器', unit: '', spec: 'ZDYZ-Z', cost: 4700),
SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
SaleQuotationItemModel(name: '矿用本安型隔离耦合器', cost: 1200),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', cost: 600),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器', cost: 400),
SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器', cost: 500),
SaleQuotationItemModel(
name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器', cost: 2000),
SaleQuotationItemModel(name: '矿用本安型支架控制器', cost: 4700),
SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
SaleQuotationItemModel(
name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组', cost: 13200),
SaleQuotationItemModel(
name: '电液换向阀(20功能20接口)', remark: '端头架主阀组', cost: 26500),
SaleQuotationItemModel(
name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm', cost: 2000),
SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'),
SaleQuotationItemModel(name: '矿用本安型位移传感器'),
SaleQuotationItemModel(name: '矿用本安型压力传感器'),

View File

@ -35,7 +35,7 @@ class SaleQuotationPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
key: controller.scaffoldKey,
endDrawer: Drawer(
endDrawer: const Drawer(
//
child: SaleQuotationEndDrawer(),
),
@ -47,7 +47,23 @@ class SaleQuotationPage extends StatelessWidget {
controller.scaffoldKey.currentState?.openEndDrawer();
},
icon: const Icon(Icons.more_horiz_outlined, color: AppTheme.white),
)
),
Container(
padding: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(10),
horizontal: ScreenAdaper.width(20)),
child: Row(mainAxisAlignment: MainAxisAlignment.end, children: [
GestureDetector(
onTap: () {
controller.addGroup();
},
child: Icon(
Icons.add,
size: ScreenAdaper.height(40),
),
)
]),
),
],
),
body: SafeArea(
@ -65,6 +81,8 @@ class SaleQuotationPage extends StatelessWidget {
(index, e) => buildBody(index))
.toList(),
))),
//
SizedBox(
height: ScreenAdaper.height(100),
child: buildTotalCostRow(),
@ -76,7 +94,7 @@ class SaleQuotationPage extends StatelessWidget {
],
),
// Positioned(
// bottom: ScreenAdaper.height(10),
// bottom: ScreenAdaper.height(220),
// right: ScreenAdaper.height(10),
// child: IconButton(
// padding: EdgeInsets.all(ScreenAdaper.height(20)),
@ -339,8 +357,8 @@ class SaleQuotationPage extends StatelessWidget {
const Spacer(),
IconButton(
onPressed: () {
ModalUtil.confirm(
title: '确定要删除此组吗?',
ModalUtil.alert(
contentText: '确定要删除此组吗?',
onConfirm: () {
controller.removeGroup(groupIndex);
});
@ -464,7 +482,7 @@ class SaleQuotationPage extends StatelessWidget {
],
),
),
VerticalDivider(),
const VerticalDivider(),
buildEditCell<int>(
Container(
alignment: Alignment.center,

View File

@ -7,6 +7,7 @@ import 'package:sk_base_mobile/constants/router.dart';
import 'package:sk_base_mobile/screens/workbench/workbench_controller.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/sk_appbar.dart';
class WorkBenchPage extends StatelessWidget {
WorkBenchPage({super.key});
@ -15,31 +16,8 @@ class WorkBenchPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: const SizedBox(),
title: const Text(
'工作台',
),
),
body: buildList()
// Container(
// padding: EdgeInsets.all(ScreenAdaper.width(20)),
// child: GridView.builder(
// shrinkWrap: true,
// physics: const NeverScrollableScrollPhysics(),
// itemCount: works.length,
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: Get.width > 800 ? 5 : 3,
// crossAxisSpacing: ScreenAdaper.width(20),
// mainAxisSpacing: ScreenAdaper.height(20),
// childAspectRatio: 1.0),
// itemBuilder: (BuildContext context, int index) {
// return buildCard(index);
// },
// ),
// )
);
appBar: const SkAppbar(title: '工作台', hideLeading: true),
body: buildList());
}
Widget buildList() {

View File

@ -3,7 +3,6 @@ import 'package:get/get.dart';
import 'package:sk_base_mobile/models/workbench.model.dart';
class WorkBenchController extends GetxController {
late final AnimationController animationController;
final List<WorkBenchModel> menus = [
WorkBenchModel(title: '库存', route: '/inventory', icon: 'inventory.svg'),
WorkBenchModel(title: '产品', route: '/product', icon: 'product.svg'),
@ -15,9 +14,4 @@ class WorkBenchController extends GetxController {
WorkBenchModel(
title: '报价计算', route: '/sale_quotation', icon: 'sale_quotation.svg'),
];
@override
void onClose() {
animationController.dispose();
super.onClose();
}
}

View File

@ -1,9 +1,26 @@
import 'dart:io';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:install_plugin/install_plugin.dart';
import 'package:package_info/package_info.dart';
import 'package:permission_handler/permission_handler.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/models/app_config.dart';
import 'package:sk_base_mobile/services/service.dart';
import 'package:sk_base_mobile/util/device.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/util/snack_bar.util.dart';
import 'package:sk_base_mobile/widgets/upgrade_confirm.dart';
import '../constants/constants.dart';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
class AppInfoService extends GetxService {
static AppInfoService get to => Get.find();
@ -12,14 +29,16 @@ class AppInfoService extends GetxService {
final scale = RxDouble(1.0);
final isLarge = RxBool(false);
final isCameraing = RxBool(false);
final downloadProgress = RxDouble(0.0);
Future<AppInfoService> init() async {
LoggerUtil()
.info("[service-appInfo] Register app-related information service");
try {
// await Future.wait(
// [getDeviceInfo(), getPackageInfo(), getAppConfig(), getossPolicy()]);
await Future.wait([
getDeviceInfo(),
getPackageInfo() /* , getAppConfig(), getossPolicy() */
]);
requestPermission();
} catch (e) {
LoggerUtil().error(e);
@ -93,6 +112,125 @@ class AppInfoService extends GetxService {
// }
// }
}
Future<void> checkVersion() async {
final res = await Future.wait([
Api.getSystemParamConfigByCode(SystemParamConfig.appVersion),
Api.getSystemParamConfigByCode(SystemParamConfig.isForceUpgrade)
]);
final newVersion = res[0].data;
final isForceUpgrade = res[1].data;
if (newVersion != null) {
final currentVersion = await DeviceUtil.getAppVersion();
if (newVersion != currentVersion) {
ModalUtil.alert(
// title: '有新版本',
builder: (_) => UpgradeConfirm(
onConfirm: () {
upgradeApp(newVersion);
},
forceUpgrade: isForceUpgrade == '1',
),
// contentText: isForceUpgrade == '1' ? '此版本非常重要,强制更新' : '请更新',
);
}
}
}
Future<void> upgradeApp(String version) async {
if (await checkPermission()) {
///
String url = '${GloablConfig.OSS_URL}/upload/shankuangtong_v$version.apk';
///
Directory? storageDir = await getExternalStorageDirectory();
if (storageDir != null) {
String storagePath = storageDir.path;
File file = File('$storagePath/shankuangtong_v${version}.apk');
if (!file.existsSync()) {
file.createSync();
}
try {
///
downloadProgress.value = 0;
Get.back();
ModalUtil.alert(
barrierDismissible: false,
contentPadding: EdgeInsets.symmetric(vertical: 0),
showActions: false,
content:
//
Obx(
() => Stack(
children: [
Container(
height: ScreenAdaper.height(40),
child: LinearProgressIndicator(
value: downloadProgress.value,
),
),
Positioned(
left: 0,
right: 0,
child: Text(
'${downloadProgress.value * 100}%',
textAlign: TextAlign.center,
style: TextStyle(fontSize: ScreenAdaper.height(30)),
))
],
),
));
await DioService.dio.download(url, file.path,
onReceiveProgress: (num received, num total) =>
onReceiveProgress(received, total),
options: Options(
responseType: ResponseType.bytes,
followRedirects: false,
));
Get.back();
InstallPlugin.install(
file.path,
).then((result) {}).catchError((error) {});
} catch (e) {
SnackBarUtil().warning('暂时无法更新,请重新下载安装, 或者请联系管理员');
}
}
}
}
///
void onReceiveProgress(num received, num total) {
if (total != -1) {
downloadProgress.value =
Decimal.parse((received / total).toStringAsFixed(2))
.toDouble()
.toPrecision(2);
}
}
///
Future<bool> checkPermission() async {
if (await DeviceUtil.getPlatForm() == 'android') {
// storage权限
if (await Permission.storage.request().isGranted) {
return true;
// PermissionStatus permission = await PermissionHandler()
// .checkPermissionStatus(PermissionGroup.storage);
// if (permission != PermissionStatus.granted) {
// Map<PermissionGroup, PermissionStatus> permissions =
// await PermissionHandler()
// .requestPermissions([PermissionGroup.storage]);
// if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {
// return true;
// }
} else {
return false;
}
}
return false;
}
T getConfigByCode<T>(String code) {
List<AppConfigItem> items = appConfig.items!;

View File

@ -64,7 +64,9 @@ class DioService extends get_package.GetxService {
} else if (e.response?.statusCode == 401) {
// dio.lock();
LoggerUtil().error('[Service-dio] logout:${e.response}');
await AuthStore().logout(force: true);
Future.delayed(Duration(seconds: 1), () {
AuthStore().logout(force: true);
});
SnackBarUtil().error('请重新登录');
} else if (e.response?.statusCode == 403) {
await SnackBarUtil().error(
@ -126,6 +128,7 @@ class DioService extends get_package.GetxService {
void onResponse(Response response, ResponseInterceptorHandler handler) async {
/* LoggerUtil().info('[Service-dio] ${response.data}'); */
if (whiteList.contains(response.requestOptions.path)) {
handler.next(response);
return;

View File

@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:dio/dio.dart' as dio_package;
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/apis/index.dart';
import 'package:sk_base_mobile/store/dict.store.dart';
@ -57,9 +58,11 @@ class AuthStore extends GetxService {
await StorageService.to.remove(CacheKeys.userInfo, isWithUser: false);
LoggerUtil().info('[Store-Auth] Logout succeed.');
// login界面
// Get已经注册
if (Get.currentRoute != RouteConfig.login) {
Get.offAllNamed(RouteConfig.login);
}
// getx跳转登录页
}
Future<void> login(
@ -72,7 +75,7 @@ class AuthStore extends GetxService {
// return;
// }
LoadingUtil.to.show();
await LoadingUtil.to.show();
TapToDismissKeyboard.dismissOf(context: Get.context!);
try {
await Future.delayed(const Duration(seconds: 1));
@ -89,7 +92,7 @@ class AuthStore extends GetxService {
Get.offNamed(RouteConfig.home);
}
} catch (e) {
await SnackBarUtil().error('账号密码错误$e');
await SnackBarUtil().error('账号密码错误');
LoggerUtil().error(e);
} finally {
LoadingUtil.to.dismiss();

View File

@ -3,7 +3,7 @@ import 'package:date_format/date_format.dart';
class DateUtil {
/// YYYY-MM-DD
static String format(DateTime date, {List<String>? formats}) {
return formatDate(date, formats ?? ['yyyy', '-', 'mm', '-', 'dd']);
return formatDate(date, formats ?? ['yyyy', '-', 'MM', '-', 'dd']);
}
///

19
lib/util/device.util.dart Normal file
View File

@ -0,0 +1,19 @@
import 'dart:io';
import 'package:package_info/package_info.dart';
class DeviceUtil {
///
static Future<String> getAppVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
return packageInfo.version;
}
///
static Future<String> getPlatForm() async {
if (Platform.isAndroid) {
return 'android';
} else {
return 'ios';
}
}
}

View File

@ -6,95 +6,134 @@ import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/core/sk_bottomsheet_picker.dart';
class ModalUtil {
static Future<bool> confirm(
{String? title, String? button, VoidCallback? onConfirm}) async {
static Future<bool> alert(
{String? title,
String? contentText,
String? confirmText,
Widget? content,
VoidCallback? onConfirm,
Widget Function(BuildContext)? builder,
EdgeInsetsGeometry? contentPadding,
bool showActions = true,
bool barrierDismissible = true,
bool showCancel = true}) async {
bool confirmed = false;
await showDialog(
context: Get.overlayContext!,
builder: (context) {
return AlertDialog(
clipBehavior: Clip.hardEdge,
backgroundColor: AppTheme.nearlyWhite,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(ScreenAdaper.sp(20)),
),
actionsPadding: EdgeInsets.zero,
titlePadding: EdgeInsets.zero,
title: title != null
? Container(
padding: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(30),
horizontal: ScreenAdaper.width(30)),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: AppTheme.dividerColor,
width: ScreenAdaper.height(2)))),
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
color: AppTheme.dangerColor,
),
SizedBox(
width: ScreenAdaper.width(10),
),
Text(
title,
style: TextStyle(
fontSize: ScreenAdaper.height(30),
fontWeight: FontWeight.w500),
)
]),
)
: null,
actions: [
Row(
children: [
Expanded(
child: InkWell(
onTap: () {
Navigator.of(context).pop();
confirmed = false;
},
child: Container(
padding: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(15)),
alignment: Alignment.center,
child: Text(
'取消',
style: TextStyle(
color: Colors.black,
fontSize: ScreenAdaper.height(30)),
)),
)),
const VerticalDivider(),
Expanded(
child: InkWell(
onTap: () {
if (onConfirm != null) onConfirm();
Navigator.pop(context);
confirmed = true;
},
child: Container(
padding:
EdgeInsets.symmetric(vertical: ScreenAdaper.height(15)),
alignment: Alignment.center,
child: Text(
'确定',
style: TextStyle(
color: Colors.black,
fontSize: ScreenAdaper.height(30)),
),
barrierDismissible: barrierDismissible,
builder: builder ??
(context) {
return AlertDialog(
clipBehavior: Clip.hardEdge,
backgroundColor: AppTheme.nearlyWhite,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(ScreenAdaper.sp(20)),
),
actionsPadding: EdgeInsets.zero,
titlePadding: EdgeInsets.zero,
contentPadding: contentPadding ??
EdgeInsets.symmetric(
vertical: ScreenAdaper.height(10),
),
)),
],
)
],
);
},
title: title != null
? Container(
padding: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(20),
),
decoration: const BoxDecoration(
border: Border(
bottom:
BorderSide(color: AppTheme.dividerColor))),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
color: AppTheme.dangerColor,
),
SizedBox(
width: ScreenAdaper.width(10),
),
Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: ScreenAdaper.height(30),
color: AppTheme.black,
),
)
],
),
)
: null,
content: content ??
(contentText != null
? Container(
padding: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(20),
),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: AppTheme.dividerColor,
width: ScreenAdaper.height(2)))),
child: Text(
// '${title}${title}${title}${title}${title}${title}${title}${title}${title}${title}${title}${title}${title}${title}${title}${title}${title}${title} ${title}${title}${title}${title}',
contentText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: ScreenAdaper.height(30),
fontWeight: FontWeight.w500),
))
: null),
actions: !showActions
? null
: [
Row(
children: [
if (showCancel)
Expanded(
child: InkWell(
onTap: () {
Navigator.of(context).pop();
confirmed = false;
},
child: Container(
padding: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(15)),
alignment: Alignment.center,
child: Text(
'取消',
style: TextStyle(
color: Colors.black,
fontSize: ScreenAdaper.height(30)),
)),
)),
Expanded(
child: InkWell(
onTap: () {
if (onConfirm != null) onConfirm();
Navigator.pop(context);
confirmed = true;
},
child: Container(
padding: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(15)),
alignment: Alignment.center,
child: Text(
confirmText ?? '确定',
style: TextStyle(
color: Colors.black,
fontSize: ScreenAdaper.height(30)),
),
),
)),
],
)
],
);
},
);
return confirmed;
}

View File

@ -59,6 +59,7 @@ class SkMutilSearchMore<T extends BaseSearchMoreModel> extends StatelessWidget {
if (isDialog) {
Get.back();
}
controller.selectedIndex.clear();
},
buttonText: '确定',
)

View File

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/util/snack_bar.util.dart';
class SkNumberInput<T> extends StatefulWidget {
final TextEditingController textController;
@ -17,8 +16,8 @@ class SkNumberInput<T> extends StatefulWidget {
final bool isRequired;
final String labelText;
final String? hint;
ValueChanged<int>? onFieldSubmitted;
SkNumberInput(
final ValueChanged<int>? onFieldSubmitted;
const SkNumberInput(
{super.key,
required this.textController,
this.onTap,

View File

@ -15,8 +15,8 @@ class SkTextInput extends StatefulWidget {
final String? Function(String?)? validator;
final EdgeInsetsGeometry? contentPadding;
final bool autoFocus;
ValueChanged<String>? onFieldSubmitted;
SkTextInput(
final ValueChanged<String>? onFieldSubmitted;
const SkTextInput(
{super.key,
required this.textController,
this.onTap,

View File

@ -30,7 +30,7 @@ class GradientButton extends StatelessWidget {
Size(double.infinity, ScreenAdaper.height(80))),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
borderRadius: BorderRadius.circular(10),
),
),
),

View File

@ -22,8 +22,8 @@ class MyAvatarWidget extends StatelessWidget {
border: Border.all(
color: AppTheme.white, width: ScreenAdaper.sp(2)),
borderRadius: BorderRadius.circular(50)),
height: ScreenAdaper.width(120),
width: ScreenAdaper.width(120),
height: ScreenAdaper.width(150),
width: ScreenAdaper.width(150),
child: const SizedBox(),
)),
Positioned(

View File

@ -0,0 +1,111 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/services/app_info.service.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/gradient_button.dart';
class UpgradeConfirm extends StatelessWidget {
void Function()? onConfirm;
bool forceUpgrade;
UpgradeConfirm({super.key, this.onConfirm, this.forceUpgrade = true});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(ScreenAdaper.radius(30))),
margin: EdgeInsets.only(bottom: ScreenAdaper.height(30)),
alignment: Alignment.center,
child: Stack(
children: [
Container(
height: ScreenAdaper.height(600),
constraints: BoxConstraints(maxHeight: ScreenAdaper.height(600)),
width: ScreenAdaper.width(800),
margin: EdgeInsets.only(
top: ScreenAdaper.height(100),
),
padding: EdgeInsets.only(
top: ScreenAdaper.height(50), bottom: ScreenAdaper.height(30)),
decoration: BoxDecoration(
color: AppTheme.nearlyWhite,
borderRadius: BorderRadius.circular(ScreenAdaper.radius(30))),
child: Column(
children: [
Text(
'有新的版本更新啦',
style: TextStyle(
decoration: TextDecoration.none,
fontSize: ScreenAdaper.height(35),
color: AppTheme.nearlyBlack,
),
),
SizedBox(
height: ScreenAdaper.height(30),
),
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
Text('1.修复bug',
style: TextStyle(
decoration: TextDecoration.none,
fontSize: ScreenAdaper.height(30),
color: AppTheme.grey,
fontWeight: FontWeight.w400)),
Text('2.优化体验',
style: TextStyle(
decoration: TextDecoration.none,
fontSize: ScreenAdaper.height(30),
color: AppTheme.grey,
fontWeight: FontWeight.w400)),
],
))),
SizedBox(
height: ScreenAdaper.height(30),
),
Container(
width: ScreenAdaper.height(400),
child: GradientButton(
buttonText: '去更新',
onPressed: () {
if (onConfirm != null) onConfirm!();
},
),
)
],
),
),
if (!forceUpgrade)
Positioned(
right: 0,
top: ScreenAdaper.height(100),
child: //
IconButton(
onPressed: () {
Get.back();
},
icon: const Icon(Icons.close)),
),
//
Positioned(
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(
image: const AssetImage('assets/images/rocket.png'),
height: ScreenAdaper.height(150),
)
],
),
),
],
),
);
}
}

View File

@ -520,6 +520,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.1+1"
install_plugin:
dependency: "direct main"
description:
name: install_plugin
sha256: "6fb67ba0781e75de4f2f2266ed25e835bfd277c5bfc2ed034af52774355857c6"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
intl:
dependency: transitive
description:

View File

@ -66,6 +66,7 @@ dependencies:
flutter_slidable: ^3.1.0
pinyin: ^3.2.0
math_expressions: ^2.4.0
install_plugin: ^2.1.0
dev_dependencies:
flutter_test:
sdk: flutter