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 "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 localPropertiesFile = rootProject.file('local.properties')
@ -50,12 +55,19 @@ android {
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// 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"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
@ -31,4 +31,18 @@
android:name="flutterEmbedding"
android:value="2" />
</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>

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:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#ffffff</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
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:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
<item name="android:windowSplashScreenIconBackgroundColor">#ffffff</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
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"/>
<subviews>
<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>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>

View File

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

BIN
keystore.jks Normal file

Binary file not shown.

View File

@ -19,7 +19,7 @@ class Api {
//
static Future<Response> getUserInfo() {
return DioService.dio.get(
Urls.getUserInfo,
Urls.userInfo,
);
}
@ -64,6 +64,12 @@ class Api {
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) {
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) {
return DioService.dio.post(Urls.saveUserInfo, data: data);
}

View File

@ -1,11 +1,14 @@
/// Global config
class GloablConfig {
// static const BASE_URL = "http://10.0.2.2:7001/api/";
static const BASE_URL = "http://192.168.60.220:7001/api/";
static const OSS_URL = "http://192.168.60.220:7001";
// 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 = "山矿通";
static const DEBUG = true;
static const PRIVACY_POLICY = 'http://h5.heeru.xyz/privacyPolicy.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 DIO_TIMEOUT = 10;
}

View File

@ -1,11 +1,11 @@
class Urls {
static String getAppConfig = 'config/getAppConfig';
static String login = 'auth/login';
static String isValidToken = 'security/isValidToken';
static String logout = 'security/logout';
static String deleteAccount = 'user/deleteAccount';
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 products = 'product';
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_screenutil/flutter_screenutil.dart';
// import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:sk_base_mobile/global.dart';
import 'package:sk_base_mobile/index.dart';
import 'package:sk_base_mobile/util/logger_util.dart';
@ -10,5 +14,23 @@ Future<void> main() async {
} catch (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 {
int? id;
String? createdAt;
String? updatedAt;
String? name;
String? value;
String? remark;
int? status;
RoleModel({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.name,
required this.value,
required this.remark,
required this.status,
required this.roleDefault,
});
RoleModel(
{this.id,
this.createdAt,
this.updatedAt,
this.name,
this.value,
this.remark,
this.status});
final int? id;
final DateTime? createdAt;
final DateTime? updatedAt;
final String? name;
final String? value;
final String? remark;
final int? status;
final dynamic roleDefault;
RoleModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
createdAt = json['createdAt'];
updatedAt = json['updatedAt'];
name = json['name'];
value = json['value'];
remark = json['remark'];
status = json['status'];
factory RoleModel.fromJson(Map<String, dynamic> json) {
return RoleModel(
id: json["id"],
createdAt: DateTime.tryParse(json["createdAt"] ?? ""),
updatedAt: DateTime.tryParse(json["updatedAt"] ?? ""),
name: json["name"],
value: json["value"],
remark: json["remark"],
status: json["status"],
roleDefault: json["default"],
);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['createdAt'] = createdAt;
data['updatedAt'] = updatedAt;
data['name'] = name;
data['value'] = value;
data['remark'] = remark;
data['status'] = status;
return data;
}
Map<String, dynamic> toJson() => {
"id": id,
"createdAt": createdAt?.toIso8601String(),
"updatedAt": updatedAt?.toIso8601String(),
"name": name,
"value": value,
"remark": remark,
"status": status,
"default": roleDefault,
};
}

View File

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

View File

@ -32,7 +32,7 @@ class CustomAppBar extends StatelessWidget {
),
Obx(
() => Text(
AuthStore.to.userInfo.value.username ?? '',
AuthStore.to.userInfo.value.nickname ?? '',
style: Theme.of(context).textTheme.titleMedium!.copyWith(
color: Colors.black,
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)),
buildForm(),
SizedBox(height: ScreenAdaper.height(50)),

View File

@ -1,6 +1,7 @@
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import '../../store/auth.store.dart';
// import 'package:sentry/sentry.dart';
class LoginController extends GetxController {
final isAgreeTerm = RxBool(false);
@ -13,6 +14,7 @@ class LoginController extends GetxController {
if (!formKey.currentState!.validate()) {
return;
}
// form中的数据
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/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/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/product_search.dart';
import 'package:sk_base_mobile/store/dict.store.dart';
@ -287,11 +289,49 @@ class NewInventoryInout extends StatelessWidget {
///
Widget buildAgent() {
return ZtTextInput(
textController: controller.agentTextController,
labelText: '经办人',
isRequired: true,
);
return TextFormField(
controller: controller.agentTextController,
decoration: InputDecoration(
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(
works[index].title,
style: TextStyle(
letterSpacing: ScreenAdaper.width(5),
letterSpacing: ScreenAdaper.width(10),
fontSize: ScreenAdaper.sp(40),
fontWeight: FontWeight.bold,
foreground: Paint()
@ -106,7 +106,7 @@ class WorkBenchPage extends StatelessWidget {
Text(
works[index].title,
style: TextStyle(
letterSpacing: ScreenAdaper.width(5),
letterSpacing: ScreenAdaper.width(10),
color: Colors.white,
fontSize: ScreenAdaper.sp(40),
fontWeight: FontWeight.bold),

View File

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

View File

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

View File

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

View File

@ -42,12 +42,7 @@ class _FadeInCacheImageState extends State<FadeInCacheImage> {
theContext = context;
if ((widget.url == null || widget.url == '' || widget.url == 'null')) {
return SizedBox(
width: widget.width,
height: widget.height,
child: Icon(Icons.image_not_supported,
size: ScreenAdaper.sp(100), color: AppTheme.grey),
);
return defaultImg();
}
return buildImg(widget.url);
@ -68,12 +63,20 @@ class _FadeInCacheImageState extends State<FadeInCacheImage> {
decoration: const BoxDecoration(color: AppTheme.grey),
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,
height: widget.height,
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/screen_adaper_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';
class ImagePreivew extends StatefulWidget {

View File

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

View File

@ -62,6 +62,7 @@ dependencies:
image_gallery_saver: ^2.0.3
dev_dependencies:
flutter_test:
sdk: flutter
@ -71,11 +72,24 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
flutter_lints: ^3.0.2
flutter_launcher_icons: ^0.13.1
flutter_native_splash: ^2.3.11
# For information on the generic Dart part of this file, see the
# 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:
android: 'launcher_icon'
ios: true

View File

@ -1,6 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<!DOCTYPE html><html><head>
<!--
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.
@ -27,7 +25,7 @@
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<link rel="icon" type="image/png" href="favicon.png">
<title>sk_base_mobile</title>
<link rel="manifest" href="manifest.json">
@ -37,7 +35,94 @@
const serviceWorkerVersion = null;
</script>
<!-- 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>
<body>
<script>
@ -55,5 +140,6 @@
});
});
</script>
</body>
</html>
</body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB