feat: 出入库登记功能完成

This commit is contained in:
louis 2024-03-28 15:13:27 +08:00
parent 39a98530fd
commit 8d699dee2f
46 changed files with 623 additions and 212 deletions

View File

@ -3,6 +3,11 @@ plugins {
id "kotlin-android" id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin" id "dev.flutter.flutter-gradle-plugin"
} }
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
def localProperties = new Properties() def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties') def localPropertiesFile = rootProject.file('local.properties')
@ -50,12 +55,19 @@ android {
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
} }
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug signingConfig signingConfigs.release
} }
} }
} }

View File

@ -21,8 +21,8 @@
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme"
/> />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.
@ -31,4 +31,18 @@
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
</application> </application>
<uses-permission
android:name="android.permission.READ_PHONE_STATE" />
<uses-permission
android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission
android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission
android:name="android.permission.CAMERA" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest> </manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

@ -6,6 +6,8 @@
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item> <item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#ffffff</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your This theme determines the color of the Android Window while your

View File

@ -6,6 +6,8 @@
<item name="android:windowFullscreen">false</item> <item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item> <item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#ffffff</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your This theme determines the color of the Android Window while your

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -1,8 +0,0 @@
flutter_native_splash:
background_image: 'assets/images/launch_image.jpg'
android: true
ios: true
landscape:
image: assets/images/launch_image_landscape.jpg
android: true
ios: true

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

@ -17,7 +17,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView> <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleAspectFill" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
</subviews> </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>

View File

@ -47,5 +47,7 @@
<true/> <true/>
<key>UIStatusBarHidden</key> <key>UIStatusBarHidden</key>
<false/> <false/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict> </dict>
</plist> </plist>

BIN
keystore.jks Normal file

Binary file not shown.

View File

@ -19,7 +19,7 @@ class Api {
// //
static Future<Response> getUserInfo() { static Future<Response> getUserInfo() {
return DioService.dio.get( return DioService.dio.get(
Urls.getUserInfo, Urls.userInfo,
); );
} }
@ -64,6 +64,12 @@ class Api {
queryParameters: {'page': 1, 'pageSize': 10, ...(params)}); queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
} }
//
static Future<Response<PaginationData>> getUsers(Map params) {
return DioService.dio.get<PaginationData>(Urls.sysUser,
queryParameters: {'page': 1, 'pageSize': 10, ...(params)});
}
// () // ()
static Future<Response> getDictTypeAll(Map params) { static Future<Response> getDictTypeAll(Map params) {
return DioService.dio.post(Urls.getDictType, data: params); return DioService.dio.post(Urls.getDictType, data: params);
@ -81,11 +87,6 @@ class Api {
); );
} }
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) { static Future<Response> saveUserInfo(Map<String, dynamic> data) {
return DioService.dio.post(Urls.saveUserInfo, data: data); return DioService.dio.post(Urls.saveUserInfo, data: data);
} }

View File

@ -1,11 +1,14 @@
/// Global config /// Global config
class GloablConfig { class GloablConfig {
// static const BASE_URL = "http://10.0.2.2:7001/api/"; // static const BASE_URL = "http://10.0.2.2:7001/api/";
static const BASE_URL = "http://192.168.60.220:7001/api/"; // static const BASE_URL = "http://144.123.43.138:3001/api/";
static const OSS_URL = "http://192.168.60.220:7001"; // 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 = "山矿通"; static const DOMAIN_NAME = "山矿通";
static const DEBUG = true; static const DEBUG = true;
static const PRIVACY_POLICY = 'http://h5.heeru.xyz/privacyPolicy.html'; static const PRIVACY_POLICY = 'http://h5.heeru.xyz/privacyPolicy.html';
static const TERM_OF_USER = 'http://h5.heeru.xyz/termConditions.html'; static const TERM_OF_USER = 'http://h5.heeru.xyz/termConditions.html';
static const APPLE_STORE_PAGE = 'https://apps.apple.com/cn/app'; static const APPLE_STORE_PAGE = 'https://apps.apple.com/cn/app';
static const DIO_TIMEOUT = 10;
} }

View File

@ -1,11 +1,11 @@
class Urls { class Urls {
static String getAppConfig = 'config/getAppConfig';
static String login = 'auth/login'; static String login = 'auth/login';
static String isValidToken = 'security/isValidToken'; static String isValidToken = 'security/isValidToken';
static String logout = 'security/logout'; static String logout = 'security/logout';
static String deleteAccount = 'user/deleteAccount'; static String deleteAccount = 'user/deleteAccount';
static String saveUserInfo = 'user/saveUserInfo'; static String saveUserInfo = 'user/saveUserInfo';
static String getUserInfo = 'account/profile'; static String sysUser = 'system/users';
static String userInfo = 'account/profile';
static String projects = 'project'; static String projects = 'project';
static String products = 'product'; static String products = 'product';
static String inventoryInout = 'materials-in-out'; static String inventoryInout = 'materials-in-out';

View File

@ -1,5 +1,9 @@
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
// import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sk_base_mobile/global.dart'; import 'package:sk_base_mobile/global.dart';
import 'package:sk_base_mobile/index.dart'; import 'package:sk_base_mobile/index.dart';
import 'package:sk_base_mobile/util/logger_util.dart'; import 'package:sk_base_mobile/util/logger_util.dart';
@ -10,5 +14,23 @@ Future<void> main() async {
} catch (e) { } catch (e) {
LoggerUtil().error('Init failed, please try again.${e}'); LoggerUtil().error('Init failed, please try again.${e}');
} }
runApp(const IndexPage()); bool isProduction = kReleaseMode;
// await SentryFlutter.init(
// (options) {
// options.environment = isProduction ? 'production' : 'development';
// // options.beforeSend = (event, {hint}) {
// // if (!isProduction) {
// // return null;
// // }
// // return event;
// // };
// options.dsn =
// 'https://ce4f75d3cd9120a1bd9f1a807e573de1@o1078619.ingest.us.sentry.io/4506981258952704';
// // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
// // We recommend adjusting this value in production.
// options.tracesSampleRate = 1.0;
// },
// appRunner: () => runApp(const IndexPage()),
// );
return runApp(const IndexPage());
} }

View File

@ -0,0 +1,33 @@
class DeptModel {
DeptModel({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.name,
required this.orderNo,
});
final int? id;
final DateTime? createdAt;
final DateTime? updatedAt;
final String? name;
final int? orderNo;
factory DeptModel.fromJson(Map<String, dynamic> json) {
return DeptModel(
id: json["id"],
createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
name: json["name"],
orderNo: json["orderNo"],
);
}
Map<String, dynamic> toJson() => {
"id": id,
"createdAt": createdAt?.toIso8601String(),
"updatedAt": updatedAt?.toIso8601String(),
"name": name,
"orderNo": orderNo,
};
}

View File

@ -1,40 +1,45 @@
class RoleModel { class RoleModel {
int? id; RoleModel({
String? createdAt; required this.id,
String? updatedAt; required this.createdAt,
String? name; required this.updatedAt,
String? value; required this.name,
String? remark; required this.value,
int? status; required this.remark,
required this.status,
required this.roleDefault,
});
RoleModel( final int? id;
{this.id, final DateTime? createdAt;
this.createdAt, final DateTime? updatedAt;
this.updatedAt, final String? name;
this.name, final String? value;
this.value, final String? remark;
this.remark, final int? status;
this.status}); final dynamic roleDefault;
RoleModel.fromJson(Map<String, dynamic> json) { factory RoleModel.fromJson(Map<String, dynamic> json) {
id = json['id']; return RoleModel(
createdAt = json['createdAt']; id: json["id"],
updatedAt = json['updatedAt']; createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
name = json['name']; updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
value = json['value']; name: json["name"],
remark = json['remark']; value: json["value"],
status = json['status']; remark: json["remark"],
status: json["status"],
roleDefault: json["default"],
);
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() => {
final Map<String, dynamic> data = <String, dynamic>{}; "id": id,
data['id'] = id; "createdAt": createdAt?.toIso8601String(),
data['createdAt'] = createdAt; "updatedAt": updatedAt?.toIso8601String(),
data['updatedAt'] = updatedAt; "name": name,
data['name'] = name; "value": value,
data['value'] = value; "remark": remark,
data['remark'] = remark; "status": status,
data['status'] = status; "default": roleDefault,
return data; };
}
} }

View File

@ -1,24 +1,13 @@
import 'package:sk_base_mobile/models/dept.model.dart';
import 'package:sk_base_mobile/models/role.model.dart'; import 'package:sk_base_mobile/models/role.model.dart';
class UserInfoModel { class UserInfoModel {
int? id; UserInfoModel({
String? createdAt; this.id,
String? updatedAt;
String? username;
String? nickname;
String? avatar;
String? qq;
String? email;
String? phone;
String? remark;
int? status;
List<RoleModel>? roles;
UserInfoModel(
{this.id,
this.createdAt, this.createdAt,
this.updatedAt, this.updatedAt,
this.username, this.username,
this.psalt,
this.nickname, this.nickname,
this.avatar, this.avatar,
this.qq, this.qq,
@ -26,44 +15,61 @@ class UserInfoModel {
this.phone, this.phone,
this.remark, this.remark,
this.status, this.status,
this.roles}); this.dept,
this.roles = const [],
UserInfoModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
createdAt = json['createdAt'];
updatedAt = json['updatedAt'];
username = json['username'];
nickname = json['nickname'];
avatar = json['avatar'];
qq = json['qq'];
email = json['email'];
phone = json['phone'];
remark = json['remark'];
status = json['status'];
if (json['roles'] != null) {
roles = <RoleModel>[];
json['roles'].forEach((v) {
roles!.add(RoleModel.fromJson(v));
}); });
}
final int? id;
final DateTime? createdAt;
final DateTime? updatedAt;
final String? username;
final String? psalt;
final String? nickname;
final String? avatar;
final String? qq;
final String? email;
final String? phone;
final String? remark;
final int? status;
final DeptModel? dept;
List<RoleModel> roles;
factory UserInfoModel.fromJson(Map<String, dynamic> json) {
return UserInfoModel(
id: json["id"],
createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
username: json["username"],
psalt: json["psalt"],
nickname: json["nickname"],
avatar: json["avatar"],
qq: json["qq"],
email: json["email"],
phone: json["phone"],
remark: json["remark"],
status: json["status"],
dept: json["dept"] == null ? null : DeptModel.fromJson(json["dept"]),
roles: json["roles"] == null
? []
: List<RoleModel>.from(
json["roles"]!.map((x) => RoleModel.fromJson(x))),
);
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() => {
final Map<String, dynamic> data = <String, dynamic>{}; "id": id,
data['id'] = id; "createdAt": createdAt?.toIso8601String(),
data['createdAt'] = createdAt; "updatedAt": updatedAt?.toIso8601String(),
data['updatedAt'] = updatedAt; "username": username,
data['username'] = username; "psalt": psalt,
data['nickname'] = nickname; "nickname": nickname,
data['avatar'] = avatar; "avatar": avatar,
data['qq'] = qq; "qq": qq,
data['email'] = email; "email": email,
data['phone'] = phone; "phone": phone,
data['remark'] = remark; "remark": remark,
data['status'] = status; "status": status,
if (roles != null) { "dept": dept?.toJson(),
data['roles'] = roles!.map((v) => v.toJson()).toList(); "roles": roles.map((x) => x?.toJson()).toList(),
} };
return data;
}
} }

View File

@ -32,7 +32,7 @@ class CustomAppBar extends StatelessWidget {
), ),
Obx( Obx(
() => Text( () => Text(
AuthStore.to.userInfo.value.username ?? '', AuthStore.to.userInfo.value.nickname ?? '',
style: Theme.of(context).textTheme.titleMedium!.copyWith( style: Theme.of(context).textTheme.titleMedium!.copyWith(
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,

View File

@ -59,17 +59,6 @@ class LoginScreen extends StatelessWidget {
) )
], ],
), ),
// Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// '山矿通',
// style: TextStyle(
// letterSpacing: ScreenAdaper.sp(10),
// fontSize: ScreenAdaper.sp(70)),
// )
// ],
// ),
SizedBox(height: ScreenAdaper.height(50)), SizedBox(height: ScreenAdaper.height(50)),
buildForm(), buildForm(),
SizedBox(height: ScreenAdaper.height(50)), SizedBox(height: ScreenAdaper.height(50)),

View File

@ -1,6 +1,7 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../store/auth.store.dart'; import '../../store/auth.store.dart';
// import 'package:sentry/sentry.dart';
class LoginController extends GetxController { class LoginController extends GetxController {
final isAgreeTerm = RxBool(false); final isAgreeTerm = RxBool(false);
@ -13,6 +14,7 @@ class LoginController extends GetxController {
if (!formKey.currentState!.validate()) { if (!formKey.currentState!.validate()) {
return; return;
} }
// form中的数据 // form中的数据
AuthStore.to.login(username: username, password: password); AuthStore.to.login(username: username, password: password);
} }

View File

@ -0,0 +1,201 @@
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/models/user_info.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/widgets/empty.dart';
import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
class AgentSearch extends StatelessWidget {
Function(UserInfoModel)? onSelected;
Function(UserInfoModel)? beforeSelectedCheck;
AgentSearch({super.key, this.onSelected, this.beforeSelectedCheck});
final controller = Get.put(AgentSearchController());
final listTitleTextStyle =
TextStyle(fontSize: ScreenAdaper.sp(20), fontWeight: FontWeight.w600);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.width(20),
vertical: ScreenAdaper.height(20)),
child: Column(
children: [
buildSearchBar(),
SizedBox(
height: ScreenAdaper.height(defaultPadding) / 2,
),
Expanded(child: buildList())
],
),
);
}
Widget buildSearchBar() {
final doSearch = debouncer((String value) {
controller.searchKey.value = value;
controller.onRefresh();
}, delayTime: 500);
return Row(
children: [
Expanded(
flex: 5,
child: TextField(
controller: controller.searchBarTextConroller,
onChanged: (value) => doSearch(value),
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(
vertical: ScreenAdaper.height(10),
horizontal: ScreenAdaper.width(10)),
hintText: '姓名',
floatingLabelBehavior: FloatingLabelBehavior.always,
prefixIcon: const Icon(Icons.search),
// searchBarController有值时不显示
suffixIcon: Obx(() => controller.searchKey.value.isEmpty
? const SizedBox()
: IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
controller.searchKey.value = '';
controller.searchBarTextConroller.clear();
doSearch('');
},
)),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
))),
],
);
}
Widget buildList() {
final textStyle = TextStyle(fontSize: ScreenAdaper.sp(22));
return Obx(() => SmartRefresher(
enablePullDown: true,
enablePullUp: true,
controller: controller.refreshController,
onLoading: controller.onLoading,
onRefresh: controller.onRefresh,
child: controller.refreshController.isLoading
? SizedBox()
: controller.list.isEmpty
? Center(
child: Empty(text: '暂无数据'),
)
: ListView.separated(
separatorBuilder: (context, index) => Divider(
color: AppTheme.dividerColor,
),
itemCount: controller.list.length,
itemBuilder: (_, index) => buildItem(index))));
}
Widget buildItem(int index) {
return InkWell(
onTap: () => {
if (beforeSelectedCheck != null)
{
if (beforeSelectedCheck!(controller.list[index]))
{onSelected!(controller.list[index])}
}
else
{onSelected!(controller.list[index])}
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.width(5),
vertical: ScreenAdaper.height(10)),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
child: FadeInCacheImage(
url: controller.list[index].avatar ?? '',
width: ScreenAdaper.width(60),
height: ScreenAdaper.width(60),
),
),
SizedBox(
width: ScreenAdaper.width(20),
),
Text(
'${controller.list[index].nickname}',
style: TextStyle(fontSize: ScreenAdaper.sp(25)),
),
if (controller.list[index].dept != null) ...[
SizedBox(
width: ScreenAdaper.width(20),
),
Container(
padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.width(10),
vertical: ScreenAdaper.height(5)),
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(ScreenAdaper.sp(10))),
child: Text('${controller.list[index].dept?.name}'),
)
]
],
),
),
);
}
}
class AgentSearchController extends GetxController {
RxList<UserInfoModel> list = RxList([]);
RxString searchKey = ''.obs;
final searchBarTextConroller = TextEditingController();
RefreshController refreshController = RefreshController(initialRefresh: true);
int page = 1;
int limit = 15;
int total = 0;
Future<List<UserInfoModel>> getData({bool isRefresh = false}) async {
if (isRefresh == true) {
page = 1;
} else {
page++;
}
final res = await Api.getUsers({
'page': page,
'pageSize': 15,
'keyword': searchKey.value,
});
List<UserInfoModel> newList =
res.data!.items.map((e) => UserInfoModel.fromJson(e)).toList();
isRefresh == true ? list.assignAll(newList) : list.addAll(newList);
return newList;
}
Future<void> onRefresh() async {
await getData(isRefresh: true).then((_) {
refreshController.refreshCompleted(resetFooterState: true);
}).catchError((_) {
refreshController.refreshFailed();
});
}
Future<void> onLoading() async {
await getData().then((_) {
if (_.isEmpty) {
refreshController.loadNoData();
} else {
refreshController.loadComplete();
}
}).catchError((_) {
refreshController.loadFailed();
});
}
}

View File

@ -7,6 +7,8 @@ import 'package:sk_base_mobile/constants/constants.dart';
import 'package:sk_base_mobile/constants/dict_enum.dart'; import 'package:sk_base_mobile/constants/dict_enum.dart';
import 'package:sk_base_mobile/models/index.dart'; import 'package:sk_base_mobile/models/index.dart';
import 'package:sk_base_mobile/models/inventory.model.dart'; import 'package:sk_base_mobile/models/inventory.model.dart';
import 'package:sk_base_mobile/models/user_info.model.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/components/agent_search.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_search.dart'; import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_search.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/components/product_search.dart'; 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/store/dict.store.dart';
@ -287,11 +289,49 @@ class NewInventoryInout extends StatelessWidget {
/// ///
Widget buildAgent() { Widget buildAgent() {
return ZtTextInput( return TextFormField(
textController: controller.agentTextController, controller: controller.agentTextController,
labelText: '经办人', decoration: InputDecoration(
isRequired: true, hintText: '请选择经办人',
); label: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"*",
style: TextStyle(
color: Colors.red, fontSize: ScreenAdaper.sp(30)),
),
Text(
'经办人',
style: TextStyle(fontSize: ScreenAdaper.sp(30)),
),
]),
floatingLabelBehavior: FloatingLabelBehavior.always),
readOnly: true,
onTap: () {
if (inOrOut == InventoryInOrOutEnum.In) {
ModalUtil.showGeneralDialog(content: AgentSearch(
onSelected: (UserInfoModel userInfo) {
Get.back();
controller.payload['agent'] = userInfo.nickname;
controller.agentTextController.text = userInfo.nickname!;
},
));
} else {
ModalUtil.showGeneralDialog(
content: AgentSearch(
onSelected: (UserInfoModel userInfo) {
Get.back();
controller.payload['agent'] = userInfo.nickname;
controller.agentTextController.text =
userInfo.nickname!;
},
),
width: Get.width - ScreenAdaper.width(50))
.then((value) => Get.delete<AgentSearchController>());
}
});
} }
/// ///

View File

@ -95,7 +95,7 @@ class WorkBenchPage extends StatelessWidget {
Text( Text(
works[index].title, works[index].title,
style: TextStyle( style: TextStyle(
letterSpacing: ScreenAdaper.width(5), letterSpacing: ScreenAdaper.width(10),
fontSize: ScreenAdaper.sp(40), fontSize: ScreenAdaper.sp(40),
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
foreground: Paint() foreground: Paint()
@ -106,7 +106,7 @@ class WorkBenchPage extends StatelessWidget {
Text( Text(
works[index].title, works[index].title,
style: TextStyle( style: TextStyle(
letterSpacing: ScreenAdaper.width(5), letterSpacing: ScreenAdaper.width(10),
color: Colors.white, color: Colors.white,
fontSize: ScreenAdaper.sp(40), fontSize: ScreenAdaper.sp(40),
fontWeight: FontWeight.bold), fontWeight: FontWeight.bold),

View File

@ -82,20 +82,20 @@ class AppInfoService extends GetxService {
} }
Future<void> getAppConfig() async { Future<void> getAppConfig() async {
if (StorageService.to.getString(CacheKeys.token, isWithUser: false) != // if (StorageService.to.getString(CacheKeys.token, isWithUser: false) !=
null) { // null) {
final response = await Api.getAppConfig(); // final response = await Api.getAppConfig();
if (response.data != null) { // if (response.data != null) {
appConfig = AppConfig.fromJson(response.data['data']); // appConfig = AppConfig.fromJson(response.data['data']);
await StorageService.to.setString( // await StorageService.to.setString(
CacheKeys.appConfig, jsonEncode(appConfig.items!), // CacheKeys.appConfig, jsonEncode(appConfig.items!),
isWithUser: false); // isWithUser: false);
if (appConfig.ver != null) { // if (appConfig.ver != null) {
await StorageService.to // await StorageService.to
.setString(CacheKeys.ver, appConfig.ver!, isWithUser: false); // .setString(CacheKeys.ver, appConfig.ver!, isWithUser: false);
} // }
} // }
} // }
} }
// need token // need token

View File

@ -16,7 +16,7 @@ class DioService extends Get.GetxService {
static late Dio _dio; static late Dio _dio;
List<String> whiteList = [Urls.login]; List<String> whiteList = [Urls.login];
BaseOptions dioBaseOptions = BaseOptions( BaseOptions dioBaseOptions = BaseOptions(
connectTimeout: const Duration(seconds: 20), connectTimeout: const Duration(seconds: GloablConfig.DIO_TIMEOUT),
baseUrl: '${GloablConfig.BASE_URL}', baseUrl: '${GloablConfig.BASE_URL}',
followRedirects: true); followRedirects: true);
@ -62,10 +62,10 @@ class DioService extends Get.GetxService {
e.response?.data['message'], e.response?.data['message'],
); );
} else if (e.response?.statusCode == 401) { } else if (e.response?.statusCode == 401) {
await SnackBarUtil().error('请重新登录');
// dio.lock(); // dio.lock();
LoggerUtil().error('[Service-dio] logout:${e.response}'); LoggerUtil().error('[Service-dio] logout:${e.response}');
await AuthStore().logout(force: true); await AuthStore().logout(force: true);
SnackBarUtil().error('请重新登录');
} else if (e.response?.statusCode == 403) { } else if (e.response?.statusCode == 403) {
await SnackBarUtil().error( await SnackBarUtil().error(
'${e.response?.data['message']}.', '${e.response?.data['message']}.',

View File

@ -25,7 +25,7 @@ class AuthStore extends GetxService {
if (token != null) { if (token != null) {
if (preUserInfo != null) { if (preUserInfo != null) {
userInfo(UserInfoModel.fromJson(jsonDecode(preUserInfo))); await getUserInfo();
LoggerUtil().info('[Store-Auth] userId: ${userInfo.value.id}'); LoggerUtil().info('[Store-Auth] userId: ${userInfo.value.id}');
} }
} }
@ -56,19 +56,11 @@ class AuthStore extends GetxService {
Future<void> logout({bool force = false}) async { Future<void> logout({bool force = false}) async {
await StorageService.to.remove(CacheKeys.token, isWithUser: false); await StorageService.to.remove(CacheKeys.token, isWithUser: false);
await StorageService.to.remove(CacheKeys.userInfo, isWithUser: false); await StorageService.to.remove(CacheKeys.userInfo, isWithUser: false);
try {
// final response = await Api.logout();
// if (response.data != null) {
LoggerUtil().info('[Store-Auth] Logout succeed.'); LoggerUtil().info('[Store-Auth] Logout succeed.');
// login界面 // login界面
if (Get.currentRoute != RouteConfig.login) { if (Get.currentRoute != RouteConfig.login) {
Get.offAllNamed(RouteConfig.login); Get.offAllNamed(RouteConfig.login);
} }
} catch (e) {
print(e);
} finally {
LoadingUtil.to.dismiss();
}
} }
Future<void> login( Future<void> login(
@ -94,11 +86,11 @@ class AuthStore extends GetxService {
.setString(CacheKeys.token, auth.token!, isWithUser: false); .setString(CacheKeys.token, auth.token!, isWithUser: false);
} }
await getUserInfo(); await getUserInfo();
await getCommonInfo();
Get.offNamed(RouteConfig.home); Get.offNamed(RouteConfig.home);
getCommonInfo();
} }
} catch (e) { } catch (e) {
await SnackBarUtil().error('账号密码错误'); await SnackBarUtil().error('账号密码错误$e');
LoggerUtil().error(e); LoggerUtil().error(e);
} finally { } finally {
LoadingUtil.to.dismiss(); LoadingUtil.to.dismiss();
@ -112,7 +104,6 @@ class AuthStore extends GetxService {
} }
Future<void> getUserInfo() async { Future<void> getUserInfo() async {
await LoadingUtil.to.show();
try { try {
final response = await Api.getUserInfo(); final response = await Api.getUserInfo();
if (response.data != null) { if (response.data != null) {
@ -121,9 +112,7 @@ class AuthStore extends GetxService {
} }
} catch (e) { } catch (e) {
SnackBarUtil().error('$e'); SnackBarUtil().error('$e');
} finally { } finally {}
LoadingUtil.to.dismiss();
}
} }
Future<void> saveUserInfo(Map<String, dynamic> data) async { Future<void> saveUserInfo(Map<String, dynamic> data) async {

View File

@ -42,12 +42,7 @@ class _FadeInCacheImageState extends State<FadeInCacheImage> {
theContext = context; theContext = context;
if ((widget.url == null || widget.url == '' || widget.url == 'null')) { if ((widget.url == null || widget.url == '' || widget.url == 'null')) {
return SizedBox( return defaultImg();
width: widget.width,
height: widget.height,
child: Icon(Icons.image_not_supported,
size: ScreenAdaper.sp(100), color: AppTheme.grey),
);
} }
return buildImg(widget.url); return buildImg(widget.url);
@ -68,12 +63,20 @@ class _FadeInCacheImageState extends State<FadeInCacheImage> {
decoration: const BoxDecoration(color: AppTheme.grey), decoration: const BoxDecoration(color: AppTheme.grey),
child: const CupertinoActivityIndicator(), child: const CupertinoActivityIndicator(),
), ),
errorWidget: (context, error, stackTrace) => SizedBox( errorWidget: (context, error, stackTrace) => defaultImg(),
);
}
Widget defaultImg() {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(), borderRadius: BorderRadius.circular(15)),
width: widget.width, width: widget.width,
height: widget.height, height: widget.height,
child: Icon(Icons.image_not_supported, child: Icon(Icons.image_not_supported,
size: ScreenAdaper.sp(100), color: AppTheme.grey), size: ScreenAdaper.sp((widget.width ?? 200) * 3 / 4),
), color: AppTheme.grey),
); );
} }
} }

View File

@ -9,7 +9,6 @@ import 'package:sk_base_mobile/util/loading_util.dart';
import 'package:sk_base_mobile/util/media_util.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/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/util/snack_bar.util.dart'; import 'package:sk_base_mobile/util/snack_bar.util.dart';
import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
import 'package:sk_base_mobile/widgets/loading_indicator.dart'; import 'package:sk_base_mobile/widgets/loading_indicator.dart';
class ImagePreivew extends StatefulWidget { class ImagePreivew extends StatefulWidget {

View File

@ -181,10 +181,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: dio name: dio
sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8" sha256: "50fec96118958b97c727d0d8f67255d3683f16cc1f90d9bc917b5d4fe3abeca9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.4.1" version: "5.4.2"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -330,10 +330,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.3" version: "3.0.2"
flutter_native_splash: flutter_native_splash:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -516,10 +516,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "3.0.0"
loading_animation_widget: loading_animation_widget:
dependency: "direct main" dependency: "direct main"
description: description:
@ -652,10 +652,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_apple name: permission_handler_apple
sha256: bdafc6db74253abb63907f4e357302e6bb786ab41465e8635f362ee71fd8707b sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.4.0" version: "9.4.4"
permission_handler_html: permission_handler_html:
dependency: transitive dependency: transitive
description: description:

View File

@ -62,6 +62,7 @@ dependencies:
image_gallery_saver: ^2.0.3 image_gallery_saver: ^2.0.3
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
@ -71,11 +72,24 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your # activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint # package. See that file for information about deactivating specific lint
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^2.0.0 flutter_lints: ^3.0.2
flutter_launcher_icons: ^0.13.1 flutter_launcher_icons: ^0.13.1
flutter_native_splash: ^2.3.11 flutter_native_splash: ^2.3.11
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
flutter_native_splash:
background_image: 'assets/images/launch_image.jpg'
android: true
ios: true
android_gravity: scaleAspectFill
ios_content_mode: scaleAspectFill
android_12:
image: assets/images/launch_image.jpg
icon_background_color: "#ffffff"
android_gravity: scaleAspectFill
ios_content_mode: scaleAspectFill
flutter_launcher_icons: flutter_launcher_icons:
android: 'launcher_icon' android: 'launcher_icon'
ios: true ios: true

View File

@ -1,6 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html><html><head>
<html>
<head>
<!-- <!--
If you are serving your web app in a path other than the root, change the If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from. href value below to reflect the base path you are serving from.
@ -27,7 +25,7 @@
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/> <link rel="icon" type="image/png" href="favicon.png">
<title>sk_base_mobile</title> <title>sk_base_mobile</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
@ -37,7 +35,94 @@
const serviceWorkerVersion = null; const serviceWorkerVersion = null;
</script> </script>
<!-- This script adds the flutter initialization JS code --> <!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script> <script src="flutter.js" defer=""></script>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<style id="splash-screen-style">
html {
height: 100%
}
body {
margin: 0;
min-height: 100%;
background-color: #ffffff;
background-image: url("splash/img/light-background.png");
background-size: 100% 100%;
}
.center {
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.contain {
display:block;
width:100%; height:100%;
object-fit: contain;
}
.stretch {
display:block;
width:100%; height:100%;
}
.cover {
display:block;
width:100%; height:100%;
object-fit: cover;
}
.bottom {
position: absolute;
bottom: 0;
left: 50%;
-ms-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
.bottomLeft {
position: absolute;
bottom: 0;
left: 0;
}
.bottomRight {
position: absolute;
bottom: 0;
right: 0;
}
</style>
<script id="splash-screen-script">
function removeSplashFromWeb() {
document.getElementById("splash")?.remove();
document.getElementById("splash-branding")?.remove();
document.body.style.background = "transparent";
}
</script>
</head> </head>
<body> <body>
<script> <script>
@ -55,5 +140,6 @@
}); });
}); });
</script> </script>
</body>
</html>
</body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB