feat: develop common widget. Hr management feature
This commit is contained in:
parent
1befdcb786
commit
93a30355cd
|
@ -15,7 +15,7 @@ class AppTheme {
|
|||
static const Color nearlyWhite = Color(0xFFFEFEFE);
|
||||
static const Color black = Color(0xFF000000);
|
||||
static const Color nearlyBlack = Color(0xFF213333);
|
||||
static const Color grey = Color.fromARGB(255, 98, 101, 102);
|
||||
static const Color grey = Color.fromARGB(255, 138, 138, 138);
|
||||
static const Color snackbarErrorBackgroudColor = Colors.red;
|
||||
static const Color snackbarSuccessBackgroudColor = Colors.green;
|
||||
static const Color snackbarWarningBackgroudColor = Colors.orange;
|
||||
|
@ -25,6 +25,9 @@ class AppTheme {
|
|||
static const Color notActiveNavigationBarColor = Colors.grey;
|
||||
static const Color dangerColor = Colors.red;
|
||||
static const Color dividerColor = Color.fromARGB(255, 224, 224, 224);
|
||||
static const Color appbarBgColor = AppTheme.primaryColor;
|
||||
static const Color scaffoldBackgroundColor = Color(0xFFf5f8ff);
|
||||
static const Color inputFillColor = Color(0xFFf5f8ff);
|
||||
}
|
||||
|
||||
final theme = ThemeData(
|
||||
|
@ -85,7 +88,7 @@ final theme = ThemeData(
|
|||
const ProgressIndicatorThemeData(color: AppTheme.primaryColor),
|
||||
dividerColor: AppTheme.dividerColor,
|
||||
cardColor: AppTheme.white,
|
||||
scaffoldBackgroundColor: AppTheme.nearlyWhite,
|
||||
scaffoldBackgroundColor: AppTheme.scaffoldBackgroundColor,
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||
backgroundColor: AppTheme.nearlyWhite,
|
||||
unselectedLabelStyle: TextStyle(fontSize: ScreenAdaper.height(20)),
|
||||
|
@ -103,7 +106,7 @@ final theme = ThemeData(
|
|||
appBarTheme: AppBarTheme(
|
||||
centerTitle: true,
|
||||
iconTheme: const IconThemeData(color: Colors.white),
|
||||
backgroundColor: AppTheme.primaryColor,
|
||||
backgroundColor: AppTheme.appbarBgColor,
|
||||
titleTextStyle: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: ScreenAdaper.height(30),
|
||||
|
@ -114,6 +117,8 @@ final theme = ThemeData(
|
|||
color: AppTheme.primaryColor,
|
||||
fontSize: ScreenAdaper.height(30),
|
||||
),
|
||||
fillColor: AppTheme.inputFillColor,
|
||||
filled: true,
|
||||
labelStyle: TextStyle(
|
||||
fontSize: ScreenAdaper.height(25),
|
||||
),
|
||||
|
|
|
@ -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 = "山矿通";
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:get/get.dart';
|
||||
import 'package:sk_base_mobile/screens/hr_manage/hr_manage.dart';
|
||||
import 'package:sk_base_mobile/screens/inventory/inventory.dart';
|
||||
import 'package:sk_base_mobile/screens/login/login.dart';
|
||||
import 'package:sk_base_mobile/screens/sale_quotation/sale_quotation.dart';
|
||||
|
@ -12,12 +13,14 @@ class RouteConfig {
|
|||
static const String userinfo = '/userinfo';
|
||||
static const String inventory = '/inventory';
|
||||
static const String saleQuotation = '/sale_quotation';
|
||||
static const String hrManage = '/hr_manage';
|
||||
|
||||
static final List<GetPage> getPages = [
|
||||
GetPage(name: login, page: () => LoginScreen()),
|
||||
GetPage(name: home, page: () => LandingPage()),
|
||||
GetPage(name: userinfo, page: () => UserInfoPage()),
|
||||
GetPage(name: inventory, page: () => const InventoryPage()),
|
||||
GetPage(name: saleQuotation, page: () => SaleQuotationPage())
|
||||
GetPage(name: saleQuotation, page: () => SaleQuotationPage()),
|
||||
GetPage(name: hrManage, page: () => HrManagePage())
|
||||
];
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:sk_base_mobile/constants/cache_key.dart';
|
|||
import 'package:sk_base_mobile/services/app_info.service.dart';
|
||||
import 'package:sk_base_mobile/services/dio.service.dart';
|
||||
import 'package:sk_base_mobile/services/storage.service.dart';
|
||||
import 'package:timeago/timeago.dart';
|
||||
import 'store/store.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
|
||||
|
@ -27,7 +28,7 @@ class Global {
|
|||
setSystemUi();
|
||||
|
||||
/// 初始化 几天前 的时区国际化
|
||||
timeago.setLocaleMessages('en', timeago.EnMessages());
|
||||
timeago.setLocaleMessages('zh_cn', MyCustomMessages());
|
||||
|
||||
/// 依赖注入Loading工具
|
||||
await Get.putAsync<LoadingUtil>(() => LoadingUtil().init());
|
||||
|
@ -69,3 +70,39 @@ class Global {
|
|||
FlutterNativeSplash.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// my_custom_messages.dart
|
||||
class MyCustomMessages implements LookupMessages {
|
||||
@override
|
||||
String prefixAgo() => '';
|
||||
@override
|
||||
String prefixFromNow() => '';
|
||||
@override
|
||||
String suffixAgo() => '';
|
||||
@override
|
||||
String suffixFromNow() => '';
|
||||
@override
|
||||
String lessThanOneMinute(int seconds) => '现在';
|
||||
@override
|
||||
String aboutAMinute(int minutes) => '$minutes分钟';
|
||||
@override
|
||||
String minutes(int minutes) => '$minutes分钟';
|
||||
@override
|
||||
String aboutAnHour(int minutes) => '$minutes分钟';
|
||||
@override
|
||||
String hours(int hours) => '$hours小时';
|
||||
@override
|
||||
String aDay(int hours) => '$hours小时';
|
||||
@override
|
||||
String days(int days) => '$days天';
|
||||
@override
|
||||
String aboutAMonth(int days) => '$days天';
|
||||
@override
|
||||
String months(int months) => '$months月';
|
||||
@override
|
||||
String aboutAYear(int year) => '$year年';
|
||||
@override
|
||||
String years(int years) => '$years年';
|
||||
@override
|
||||
String wordSeparator() => ' ';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
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/user_info.model.dart';
|
||||
import 'package:sk_base_mobile/util/date.util.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/core/sk_ink.dart';
|
||||
import 'package:sk_base_mobile/widgets/core/sk_tag.dart';
|
||||
import 'package:sk_base_mobile/widgets/core/sk_text_input.dart';
|
||||
import 'package:sk_base_mobile/widgets/empty.dart';
|
||||
import 'package:sk_base_mobile/widgets/sk_appbar.dart';
|
||||
|
||||
class HrManagePage extends StatelessWidget {
|
||||
final controller = Get.put(HrManageController());
|
||||
HrManagePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
// 取消焦点
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: Color(0xFFe9f0fd),
|
||||
appBar: SkAppbar(
|
||||
backgroundColor: AppTheme.nearlyWhite,
|
||||
iconAndTextColor: AppTheme.black,
|
||||
title: '人事管理',
|
||||
),
|
||||
body: buildBody(),
|
||||
));
|
||||
}
|
||||
|
||||
Widget buildBody() {
|
||||
return Column(
|
||||
children: [
|
||||
buildSearchBar(),
|
||||
Expanded(
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: ScreenAdaper.height(defaultPadding),
|
||||
vertical: ScreenAdaper.height(defaultPadding)),
|
||||
child: Obx(
|
||||
() => SmartRefresher(
|
||||
enablePullDown: true,
|
||||
enablePullUp: true,
|
||||
controller: controller.refreshController,
|
||||
onLoading: controller.onLoading,
|
||||
onRefresh: controller.onRefresh,
|
||||
child: controller.list.isEmpty
|
||||
? const Center(
|
||||
child: Empty(text: '暂无数据'),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
return buildUserCard(index);
|
||||
},
|
||||
itemCount: controller.list.length,
|
||||
)),
|
||||
)),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildSearchBar() {
|
||||
final doSearch = debouncer((String value) {
|
||||
controller.searchKey.value = value;
|
||||
controller.onRefresh();
|
||||
}, delayTime: 500);
|
||||
return Container(
|
||||
color: AppTheme.nearlyWhite,
|
||||
padding: EdgeInsets.only(
|
||||
right: ScreenAdaper.width(20),
|
||||
left: ScreenAdaper.width(20),
|
||||
bottom: ScreenAdaper.height(20)),
|
||||
child: Row(children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: ScreenAdaper.height(70),
|
||||
child: SkTextInput(
|
||||
textController: controller.searchBarTextConroller,
|
||||
onChanged: (value) => doSearch(value),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
||||
prefix: Icon(
|
||||
Icons.search,
|
||||
color: AppTheme.nearlyBlack,
|
||||
size: ScreenAdaper.height(40),
|
||||
), // 当searchBarController有值时不显示
|
||||
suffixIcon: Obx(() => controller.searchKey.value.isEmpty
|
||||
? const SizedBox()
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
onPressed: () {
|
||||
controller.searchKey.value = '';
|
||||
controller.searchBarTextConroller.clear();
|
||||
doSearch('');
|
||||
},
|
||||
)),
|
||||
hint: '查询员工',
|
||||
isDense: true,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(vertical: ScreenAdaper.height(10)),
|
||||
),
|
||||
)),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.width(10),
|
||||
),
|
||||
SkInk(
|
||||
border: Border.all(color: AppTheme.grey.withOpacity(0.8)),
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
|
||||
onTap: () {},
|
||||
child: SizedBox(
|
||||
width: ScreenAdaper.height(65),
|
||||
height: ScreenAdaper.height(65),
|
||||
child: Icon(
|
||||
Icons.filter_list_sharp,
|
||||
size: ScreenAdaper.height(50),
|
||||
),
|
||||
),
|
||||
)
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildUserCard(int index) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: ScreenAdaper.height(defaultPadding),
|
||||
vertical: ScreenAdaper.height(defaultPadding)),
|
||||
margin: EdgeInsets.only(bottom: ScreenAdaper.height(defaultPadding)),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme.white,
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// 头像
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
child: Image(
|
||||
height: ScreenAdaper.height(80),
|
||||
image: NetworkImage(
|
||||
'https://thirdqq.qlogo.cn/g?b=qq&s=100&nk=1743369777')),
|
||||
),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.height(defaultPadding),
|
||||
),
|
||||
// 中间信息
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('${controller.list[index].nickname}',
|
||||
style: TextStyle(
|
||||
fontSize: ScreenAdaper.height(30),
|
||||
fontWeight: FontWeight.w600)),
|
||||
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(5),
|
||||
), // role
|
||||
Text('${controller.list[index].dept?.name}',
|
||||
style: TextStyle(color: AppTheme.grey)),
|
||||
SizedBox(
|
||||
height: ScreenAdaper.height(5),
|
||||
),
|
||||
Container(
|
||||
child: Wrap(
|
||||
spacing: ScreenAdaper.height(5),
|
||||
runSpacing: ScreenAdaper.height(5),
|
||||
children: [
|
||||
...controller.list[index].roles.map((e) => SkTag(
|
||||
text: '${e.name}', color: AppTheme.primaryColorLight))
|
||||
],
|
||||
)),
|
||||
],
|
||||
)),
|
||||
|
||||
/// 右侧action
|
||||
/// 电话
|
||||
buildActionButton(
|
||||
onTap: () {},
|
||||
icon: Icons.phone,
|
||||
),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.height(defaultPadding),
|
||||
),
|
||||
|
||||
/// 邮件
|
||||
buildActionButton(
|
||||
onTap: () {},
|
||||
icon: Icons.email_outlined,
|
||||
),
|
||||
SizedBox(
|
||||
width: ScreenAdaper.height(defaultPadding),
|
||||
),
|
||||
|
||||
/// 编辑
|
||||
buildActionButton(
|
||||
onTap: () {},
|
||||
icon: Icons.edit,
|
||||
)
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'工龄: ${SkDateUtil.howLongAgo(controller.list[index].createdAt!)}'),
|
||||
const Spacer(),
|
||||
const SkTag(
|
||||
text: '在职',
|
||||
color: Colors.green,
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildActionButton({
|
||||
required Function() onTap,
|
||||
required IconData icon,
|
||||
}) {
|
||||
return SkInk(
|
||||
onTap: () {},
|
||||
border: Border.all(color: AppTheme.grey.withOpacity(0.8)),
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(ScreenAdaper.height(5)),
|
||||
child: Icon(
|
||||
icon,
|
||||
size: ScreenAdaper.height(35),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HrManageController 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();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ class Dates extends StatelessWidget {
|
|||
children: [
|
||||
Obx(
|
||||
() => Text(
|
||||
DateUtil.getMonth(
|
||||
SkDateUtil.getMonth(
|
||||
controller.endTime.value.add(Duration(days: -index))),
|
||||
style: TextStyle(
|
||||
color: controller.currentIndex.value == index
|
||||
|
@ -27,7 +27,7 @@ class Dates extends StatelessWidget {
|
|||
),
|
||||
Obx(
|
||||
() => Text(
|
||||
DateUtil.getDate(
|
||||
SkDateUtil.getDate(
|
||||
controller.endTime.value.add(Duration(days: -index))),
|
||||
style: TextStyle(
|
||||
color: controller.currentIndex.value == index
|
||||
|
@ -40,7 +40,7 @@ class Dates extends StatelessWidget {
|
|||
),
|
||||
Obx(
|
||||
() => Text(
|
||||
DateUtil.getDay(
|
||||
SkDateUtil.getDay(
|
||||
controller.endTime.value.add(Duration(days: -index))),
|
||||
style: TextStyle(
|
||||
color: controller.currentIndex.value == index
|
||||
|
|
|
@ -109,7 +109,7 @@ class InventoryInoutCard extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Text(
|
||||
'${DateUtil.format(controller.list[ind][index].time!, formats: [
|
||||
'${SkDateUtil.format(controller.list[ind][index].time!, formats: [
|
||||
'HH',
|
||||
':',
|
||||
"nn"
|
||||
|
|
|
@ -62,7 +62,7 @@ class InventoryInoutInfo extends StatelessWidget {
|
|||
],
|
||||
buildListItem(
|
||||
leading: '出入库时间',
|
||||
trailing: DateUtil.format(
|
||||
trailing: SkDateUtil.format(
|
||||
controller.inventoryInoutInfo.value!.time!,
|
||||
formats: [
|
||||
'yyyy',
|
||||
|
|
|
@ -247,8 +247,8 @@ class InventoryInoutController extends GetxController {
|
|||
Future<List<InventoryInOutModel>> getInoutHistory() async {
|
||||
loading.value = true;
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
final selectedDate =
|
||||
DateUtil.format(endTime.value.add(Duration(days: -currentIndex.value)));
|
||||
final selectedDate = SkDateUtil.format(
|
||||
endTime.value.add(Duration(days: -currentIndex.value)));
|
||||
try {
|
||||
final res = await Api.getInventoryInout({
|
||||
'time': [selectedDate, selectedDate]
|
||||
|
@ -290,7 +290,7 @@ class InventoryInoutController extends GetxController {
|
|||
}
|
||||
|
||||
getDateAccordingTabs(int value) {
|
||||
return DateUtil.format(
|
||||
return SkDateUtil.format(
|
||||
endTime.value.add(Duration(days: -(daysNum - value))));
|
||||
}
|
||||
|
||||
|
@ -300,7 +300,7 @@ class InventoryInoutController extends GetxController {
|
|||
RxList<InventoryInOutModel> tempList1 = <InventoryInOutModel>[].obs;
|
||||
tempList1.clear();
|
||||
for (int j = 0; j < model.length; j++) {
|
||||
final sourceDateStr = DateUtil.format(model[j].time);
|
||||
final sourceDateStr = SkDateUtil.format(model[j].time);
|
||||
final currentDateStr = getDateAccordingTabs(i);
|
||||
if (sourceDateStr == currentDateStr) {
|
||||
tempList1.add(model[j]);
|
||||
|
|
|
@ -56,7 +56,7 @@ class MineSettingsPage extends StatelessWidget {
|
|||
EdgeInsets.symmetric(vertical: ScreenAdaper.width(10)),
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
await AppInfoService.to.checkVersion();
|
||||
await AppInfoService.to.checkVersion(forceCheck: true);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
|
|
|
@ -83,9 +83,7 @@ class AgentSearch extends StatelessWidget {
|
|||
controller: controller.refreshController,
|
||||
onLoading: controller.onLoading,
|
||||
onRefresh: controller.onRefresh,
|
||||
child: controller.refreshController.isLoading
|
||||
? const SizedBox()
|
||||
: controller.list.isEmpty
|
||||
child: controller.list.isEmpty
|
||||
? const Center(
|
||||
child: Empty(text: '暂无数据'),
|
||||
)
|
||||
|
|
|
@ -283,7 +283,7 @@ class NewInventoryInout extends StatelessWidget {
|
|||
},
|
||||
onDateSelected: (date) {
|
||||
if (date != null) {
|
||||
controller.dateTextController.text = DateUtil.format(date);
|
||||
controller.dateTextController.text = SkDateUtil.format(date);
|
||||
controller.payload['time'] = controller.dateTextController.text;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -74,8 +74,8 @@ class NewInventoryInoutController extends GetxController {
|
|||
@override
|
||||
onReady() {
|
||||
super.onReady();
|
||||
dateTextController.text = DateUtil.format(DateTime.now());
|
||||
payload['time'] = DateUtil.format(DateTime.now());
|
||||
dateTextController.text = SkDateUtil.format(DateTime.now());
|
||||
payload['time'] = SkDateUtil.format(DateTime.now());
|
||||
payload['inOrOut'] = inOrOut;
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,7 @@ class NewInventoryInoutController extends GetxController {
|
|||
// return;
|
||||
// }
|
||||
|
||||
payload['time'] = DateUtil.format(DateTime.now(), formats: [
|
||||
payload['time'] = SkDateUtil.format(DateTime.now(), formats: [
|
||||
'yyyy',
|
||||
'-',
|
||||
'mm',
|
||||
|
@ -274,7 +274,7 @@ class NewInventoryInoutController extends GetxController {
|
|||
await showTimePicker(context: context, initialTime: TimeOfDay.now());
|
||||
if (picker != null) {
|
||||
startTime.value =
|
||||
'${DateUtil.addPrefix(picker.hourOfPeriod.toString())}:${DateUtil.addPrefix(picker.minute.toString())}:${picker.period.name.toUpperCase()}';
|
||||
'${SkDateUtil.addPrefix(picker.hourOfPeriod.toString())}:${SkDateUtil.addPrefix(picker.minute.toString())}:${picker.period.name.toUpperCase()}';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,7 +283,7 @@ class NewInventoryInoutController extends GetxController {
|
|||
await showTimePicker(context: context, initialTime: TimeOfDay.now());
|
||||
if (picker != null) {
|
||||
endTime.value =
|
||||
'${DateUtil.addPrefix(picker.hourOfPeriod.toString())}:${DateUtil.addPrefix(picker.minute.toString())}:${picker.period.name.toUpperCase()}';
|
||||
'${SkDateUtil.addPrefix(picker.hourOfPeriod.toString())}:${SkDateUtil.addPrefix(picker.minute.toString())}:${picker.period.name.toUpperCase()}';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,7 +296,7 @@ class NewInventoryInoutController extends GetxController {
|
|||
if (picker != null) {
|
||||
pickedDate = picker;
|
||||
selectedDate.value =
|
||||
'${DateUtil.addPrefix(picker.day.toString())}/${DateUtil.addPrefix(picker.month.toString())}/${picker.year}';
|
||||
'${SkDateUtil.addPrefix(picker.day.toString())}/${SkDateUtil.addPrefix(picker.month.toString())}/${picker.year}';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,9 +82,7 @@ class SaleQuotationGroupSearch extends StatelessWidget {
|
|||
controller: controller.refreshController,
|
||||
onLoading: controller.onLoading,
|
||||
onRefresh: controller.onRefresh,
|
||||
child: controller.refreshController.isLoading
|
||||
? const SizedBox()
|
||||
: controller.list.isEmpty
|
||||
child: controller.list.isEmpty
|
||||
? const Center(
|
||||
child: Empty(text: '暂无数据'),
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@ class WorkBenchController extends GetxController {
|
|||
WorkBenchModel(title: '库存', route: '/inventory', icon: 'inventory.svg'),
|
||||
WorkBenchModel(title: '产品', route: '/product', icon: 'product.svg'),
|
||||
WorkBenchModel(title: '合同', route: '/contract', icon: 'contract.svg'),
|
||||
WorkBenchModel(title: '人事', route: '/hr', icon: 'hr.svg'),
|
||||
WorkBenchModel(title: '人事', route: '/hr_manage', icon: 'hr.svg'),
|
||||
WorkBenchModel(title: '公车', route: '/vehicle', icon: 'vehicle.svg'),
|
||||
WorkBenchModel(title: '任务', route: '/task_manage', icon: 'task_manage.svg'),
|
||||
WorkBenchModel(title: '报表', route: '/report', icon: 'report.svg'),
|
||||
|
|
|
@ -112,7 +112,7 @@ class AppInfoService extends GetxService {
|
|||
// }
|
||||
// }
|
||||
}
|
||||
Future<void> checkVersion() async {
|
||||
Future<void> checkVersion({forceCheck = false}) async {
|
||||
final res = await Future.wait([
|
||||
Api.getSystemParamConfigByCode(SystemParamConfig.appVersion),
|
||||
Api.getSystemParamConfigByCode(SystemParamConfig.isForceUpgrade)
|
||||
|
@ -132,6 +132,8 @@ class AppInfoService extends GetxService {
|
|||
),
|
||||
// contentText: isForceUpgrade == '1' ? '此版本非常重要,强制更新' : '请更新',
|
||||
);
|
||||
} else if (forceCheck) {
|
||||
SnackBarUtil().info('已经是最新版本');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import 'package:date_format/date_format.dart';
|
||||
import 'package:timeago/timeago.dart' as timeago;
|
||||
|
||||
class DateUtil {
|
||||
class SkDateUtil {
|
||||
/// 格式化日期 默认 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']);
|
||||
}
|
||||
|
||||
/// 获取几月
|
||||
|
@ -33,4 +34,9 @@ class DateUtil {
|
|||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
/// 使用timeago距现在多久
|
||||
static String howLongAgo(DateTime date) {
|
||||
return timeago.format(date, locale: 'zh_cn'); //
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ class SnackBarUtil {
|
|||
borderRadius: 15,
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: ScreenAdaper.height(15), vertical: 0),
|
||||
duration: const Duration(seconds: 2),
|
||||
duration: const Duration(seconds: 1),
|
||||
dismissDirection: DismissDirection.horizontal,
|
||||
forwardAnimationCurve: Curves.fastLinearToSlowEaseIn,
|
||||
reverseAnimationCurve: Curves.linearToEaseOut);
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sk_base_mobile/app_theme.dart';
|
||||
|
||||
class SkInk extends StatelessWidget {
|
||||
final Widget? child;
|
||||
final void Function()? onTap;
|
||||
final BorderRadius? borderRadius;
|
||||
final BoxBorder? border;
|
||||
const SkInk(
|
||||
{super.key, this.child, this.onTap, this.borderRadius, this.border});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
border: border ?? Border.all(color: AppTheme.grey.withOpacity(0.8)),
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
child:
|
||||
InkWell(borderRadius: borderRadius, onTap: onTap, child: child)),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -141,9 +141,7 @@ class SkMutilSearchMore<T extends BaseSearchMoreModel> extends StatelessWidget {
|
|||
controller: controller.refreshController,
|
||||
onLoading: controller.onLoading,
|
||||
onRefresh: controller.onRefresh,
|
||||
child: controller.refreshController.isLoading
|
||||
? const SizedBox()
|
||||
: controller.list.isEmpty
|
||||
child: controller.list.isEmpty
|
||||
? const Center(
|
||||
child: Empty(text: '暂无数据'),
|
||||
)
|
||||
|
|
|
@ -125,9 +125,7 @@ class SkSingleSearchMore<T> extends StatelessWidget {
|
|||
controller: controller.refreshController,
|
||||
onLoading: controller.onLoading,
|
||||
onRefresh: controller.onRefresh,
|
||||
child: controller.refreshController.isLoading
|
||||
? const SizedBox()
|
||||
: controller.list.isEmpty
|
||||
child: controller.list.isEmpty
|
||||
? const Center(
|
||||
child: Empty(text: '暂无数据'),
|
||||
)
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sk_base_mobile/app_theme.dart';
|
||||
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
||||
|
||||
class SkTag extends StatelessWidget {
|
||||
final String text;
|
||||
final Color? color;
|
||||
const SkTag({super.key, required this.text, required this.color});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: ScreenAdaper.height(10),
|
||||
vertical: ScreenAdaper.height(4)),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: (color ?? AppTheme.primaryColorLight).withOpacity(0.1)),
|
||||
color: (color ?? AppTheme.primaryColorLight).withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: color ?? AppTheme.primaryColorLight,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: ScreenAdaper.sp(30)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ class SkTextInput extends StatefulWidget {
|
|||
final TextEditingController textController;
|
||||
final Function(FocusNode)? onTap;
|
||||
final bool isRequired;
|
||||
final String labelText;
|
||||
final String? labelText;
|
||||
final String? hint;
|
||||
final bool isTextArea;
|
||||
final bool isDense;
|
||||
|
@ -16,6 +16,10 @@ class SkTextInput extends StatefulWidget {
|
|||
final EdgeInsetsGeometry? contentPadding;
|
||||
final bool autoFocus;
|
||||
final ValueChanged<String>? onFieldSubmitted;
|
||||
final Icon? prefix;
|
||||
final Widget? suffixIcon;
|
||||
final InputBorder? border;
|
||||
final FloatingLabelBehavior? floatingLabelBehavior;
|
||||
const SkTextInput(
|
||||
{super.key,
|
||||
required this.textController,
|
||||
|
@ -24,8 +28,12 @@ class SkTextInput extends StatefulWidget {
|
|||
this.onFieldSubmitted,
|
||||
this.isRequired = false,
|
||||
this.onTapOutside,
|
||||
this.labelText = '',
|
||||
this.labelText,
|
||||
this.prefix,
|
||||
this.suffixIcon,
|
||||
this.onChanged,
|
||||
this.border,
|
||||
this.floatingLabelBehavior = FloatingLabelBehavior.always,
|
||||
this.isTextArea = false,
|
||||
this.autoFocus = false,
|
||||
this.contentPadding,
|
||||
|
@ -74,12 +82,17 @@ class _SkTextInputState extends State<SkTextInput> {
|
|||
onFieldSubmitted: widget.onFieldSubmitted,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
validator: widget.validator,
|
||||
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: widget.prefix,
|
||||
suffixIcon: widget.suffixIcon,
|
||||
errorStyle: const TextStyle(fontSize: 0, height: 0.01),
|
||||
contentPadding: widget.contentPadding,
|
||||
isDense: widget.isDense,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
label: Row(
|
||||
border: widget.border,
|
||||
floatingLabelBehavior: widget.floatingLabelBehavior,
|
||||
label: widget.labelText != null
|
||||
? Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
@ -87,13 +100,15 @@ class _SkTextInputState extends State<SkTextInput> {
|
|||
Text(
|
||||
"*",
|
||||
style: TextStyle(
|
||||
color: Colors.red, fontSize: ScreenAdaper.height(30)),
|
||||
color: Colors.red,
|
||||
fontSize: ScreenAdaper.height(30)),
|
||||
),
|
||||
Text(
|
||||
widget.labelText,
|
||||
widget.labelText!,
|
||||
style: TextStyle(fontSize: ScreenAdaper.height(30)),
|
||||
),
|
||||
]),
|
||||
])
|
||||
: null,
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: AppTheme.primaryColorLight, width: 2),
|
||||
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
||||
|
|
|
@ -1,22 +1,39 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sk_base_mobile/app_theme.dart';
|
||||
import 'package:sk_base_mobile/util/util.dart';
|
||||
|
||||
class SkAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
final String title;
|
||||
final List<Widget>? action;
|
||||
final bool hideLeading;
|
||||
final PreferredSizeWidget? bottom;
|
||||
final Color? backgroundColor;
|
||||
final Color? iconAndTextColor;
|
||||
const SkAppbar(
|
||||
{super.key, required this.title, this.action, this.hideLeading = false});
|
||||
{super.key,
|
||||
required this.title,
|
||||
this.action,
|
||||
this.hideLeading = false,
|
||||
this.backgroundColor,
|
||||
this.iconAndTextColor,
|
||||
this.bottom});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
titleTextStyle: TextStyle(
|
||||
color: iconAndTextColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: ScreenAdaper.width(2)),
|
||||
backgroundColor: backgroundColor,
|
||||
bottom: bottom,
|
||||
leading: hideLeading
|
||||
? const SizedBox()
|
||||
: IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_back_ios,
|
||||
size: ScreenAdaper.height(40),
|
||||
color: iconAndTextColor,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
|
|
Loading…
Reference in New Issue