diff --git a/lib/app_theme.dart b/lib/app_theme.dart index 965b94d..07b7cbd 100644 --- a/lib/app_theme.dart +++ b/lib/app_theme.dart @@ -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), ), diff --git a/lib/config.dart b/lib/config.dart index 02b0257..605fa68 100644 --- a/lib/config.dart +++ b/lib/config.dart @@ -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 = "山矿通"; diff --git a/lib/constants/router.dart b/lib/constants/router.dart index 21901e9..9a5fc54 100644 --- a/lib/constants/router.dart +++ b/lib/constants/router.dart @@ -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 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()) ]; } diff --git a/lib/global.dart b/lib/global.dart index 46fda14..9da3c04 100644 --- a/lib/global.dart +++ b/lib/global.dart @@ -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().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() => ' '; +} diff --git a/lib/screens/hr_manage/hr_manage.dart b/lib/screens/hr_manage/hr_manage.dart new file mode 100644 index 0000000..d10871e --- /dev/null +++ b/lib/screens/hr_manage/hr_manage.dart @@ -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 list = RxList([]); + RxString searchKey = ''.obs; + final searchBarTextConroller = TextEditingController(); + RefreshController refreshController = RefreshController(initialRefresh: true); + int page = 1; + int limit = 15; + int total = 0; + Future> 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 newList = + res.data!.items.map((e) => UserInfoModel.fromJson(e)).toList(); + isRefresh == true ? list.assignAll(newList) : list.addAll(newList); + + return newList; + } + + Future onRefresh() async { + await getData(isRefresh: true).then((_) { + refreshController.refreshCompleted(resetFooterState: true); + }).catchError((_) { + refreshController.refreshFailed(); + }); + } + + Future onLoading() async { + await getData().then((_) { + if (_.isEmpty) { + refreshController.loadNoData(); + } else { + refreshController.loadComplete(); + } + }).catchError((_) { + refreshController.loadFailed(); + }); + } +} diff --git a/lib/screens/inventory_inout/components/dates.dart b/lib/screens/inventory_inout/components/dates.dart index 28d58b7..e1163ae 100644 --- a/lib/screens/inventory_inout/components/dates.dart +++ b/lib/screens/inventory_inout/components/dates.dart @@ -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 diff --git a/lib/screens/inventory_inout/components/inventory_inout_card.dart b/lib/screens/inventory_inout/components/inventory_inout_card.dart index 3026a68..a2a509b 100644 --- a/lib/screens/inventory_inout/components/inventory_inout_card.dart +++ b/lib/screens/inventory_inout/components/inventory_inout_card.dart @@ -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" diff --git a/lib/screens/inventory_inout/components/inventory_inout_info.dart b/lib/screens/inventory_inout/components/inventory_inout_info.dart index b7c529e..f47d6fc 100644 --- a/lib/screens/inventory_inout/components/inventory_inout_info.dart +++ b/lib/screens/inventory_inout/components/inventory_inout_info.dart @@ -62,7 +62,7 @@ class InventoryInoutInfo extends StatelessWidget { ], buildListItem( leading: '出入库时间', - trailing: DateUtil.format( + trailing: SkDateUtil.format( controller.inventoryInoutInfo.value!.time!, formats: [ 'yyyy', diff --git a/lib/screens/inventory_inout/inventory_inout_controller.dart b/lib/screens/inventory_inout/inventory_inout_controller.dart index 4042325..05c4225 100644 --- a/lib/screens/inventory_inout/inventory_inout_controller.dart +++ b/lib/screens/inventory_inout/inventory_inout_controller.dart @@ -247,8 +247,8 @@ class InventoryInoutController extends GetxController { Future> 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 tempList1 = [].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]); diff --git a/lib/screens/mine/settings/mine_settings.dart b/lib/screens/mine/settings/mine_settings.dart index 87706c1..4442656 100644 --- a/lib/screens/mine/settings/mine_settings.dart +++ b/lib/screens/mine/settings/mine_settings.dart @@ -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( diff --git a/lib/screens/new_inventory_inout/components/agent_search.dart b/lib/screens/new_inventory_inout/components/agent_search.dart index d12e452..47e7aab 100644 --- a/lib/screens/new_inventory_inout/components/agent_search.dart +++ b/lib/screens/new_inventory_inout/components/agent_search.dart @@ -83,18 +83,16 @@ class AgentSearch extends StatelessWidget { controller: controller.refreshController, onLoading: controller.onLoading, onRefresh: controller.onRefresh, - child: controller.refreshController.isLoading - ? const SizedBox() - : controller.list.isEmpty - ? const Center( - child: Empty(text: '暂无数据'), - ) - : ListView.separated( - separatorBuilder: (context, index) => const Divider( - color: AppTheme.dividerColor, - ), - itemCount: controller.list.length, - itemBuilder: (_, index) => buildItem(index)))); + child: controller.list.isEmpty + ? const Center( + child: Empty(text: '暂无数据'), + ) + : ListView.separated( + separatorBuilder: (context, index) => const Divider( + color: AppTheme.dividerColor, + ), + itemCount: controller.list.length, + itemBuilder: (_, index) => buildItem(index)))); } Widget buildItem(int index) { diff --git a/lib/screens/new_inventory_inout/new_inventory_inout.dart b/lib/screens/new_inventory_inout/new_inventory_inout.dart index 029cbdc..0110f9c 100644 --- a/lib/screens/new_inventory_inout/new_inventory_inout.dart +++ b/lib/screens/new_inventory_inout/new_inventory_inout.dart @@ -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; } }, diff --git a/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart b/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart index 2a98f89..4e818c1 100644 --- a/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart +++ b/lib/screens/new_inventory_inout/new_inventory_inout_controller.dart @@ -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}'; } } diff --git a/lib/screens/sale_quotation/components/sale_quotation_group_search.dart b/lib/screens/sale_quotation/components/sale_quotation_group_search.dart index d0c5c15..36f7a0f 100644 --- a/lib/screens/sale_quotation/components/sale_quotation_group_search.dart +++ b/lib/screens/sale_quotation/components/sale_quotation_group_search.dart @@ -82,18 +82,16 @@ class SaleQuotationGroupSearch extends StatelessWidget { controller: controller.refreshController, onLoading: controller.onLoading, onRefresh: controller.onRefresh, - child: controller.refreshController.isLoading - ? const SizedBox() - : controller.list.isEmpty - ? const Center( - child: Empty(text: '暂无数据'), - ) - : ListView.separated( - separatorBuilder: (context, index) => const Divider( - color: AppTheme.dividerColor, - ), - itemCount: controller.list.length, - itemBuilder: (_, index) => buildItem(index)))); + child: controller.list.isEmpty + ? const Center( + child: Empty(text: '暂无数据'), + ) + : ListView.separated( + separatorBuilder: (context, index) => const Divider( + color: AppTheme.dividerColor, + ), + itemCount: controller.list.length, + itemBuilder: (_, index) => buildItem(index)))); } Widget buildItem(int index) { diff --git a/lib/screens/workbench/workbench_controller.dart b/lib/screens/workbench/workbench_controller.dart index 4603dad..f0562c9 100644 --- a/lib/screens/workbench/workbench_controller.dart +++ b/lib/screens/workbench/workbench_controller.dart @@ -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'), diff --git a/lib/services/app_info.service.dart b/lib/services/app_info.service.dart index 4eea238..4323cd7 100644 --- a/lib/services/app_info.service.dart +++ b/lib/services/app_info.service.dart @@ -112,7 +112,7 @@ class AppInfoService extends GetxService { // } // } } - Future checkVersion() async { + Future 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('已经是最新版本'); } } } diff --git a/lib/util/date.util.dart b/lib/util/date.util.dart index aae1a53..930cd7b 100644 --- a/lib/util/date.util.dart +++ b/lib/util/date.util.dart @@ -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? 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'); // + } } diff --git a/lib/util/snack_bar.util.dart b/lib/util/snack_bar.util.dart index 1b958bb..689d00f 100644 --- a/lib/util/snack_bar.util.dart +++ b/lib/util/snack_bar.util.dart @@ -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); diff --git a/lib/widgets/core/sk_ink.dart b/lib/widgets/core/sk_ink.dart new file mode 100644 index 0000000..b6e01a1 --- /dev/null +++ b/lib/widgets/core/sk_ink.dart @@ -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)), + ); + } +} diff --git a/lib/widgets/core/sk_muti_search_more.dart b/lib/widgets/core/sk_muti_search_more.dart index d741444..6e97cf0 100644 --- a/lib/widgets/core/sk_muti_search_more.dart +++ b/lib/widgets/core/sk_muti_search_more.dart @@ -141,19 +141,17 @@ class SkMutilSearchMore extends StatelessWidget { controller: controller.refreshController, onLoading: controller.onLoading, onRefresh: controller.onRefresh, - child: controller.refreshController.isLoading - ? const SizedBox() - : controller.list.isEmpty - ? const Center( - child: Empty(text: '暂无数据'), - ) - : ListView.separated( - separatorBuilder: (context, index) => const Divider( - color: AppTheme.dividerColor, - height: 1, - ), - itemCount: controller.list.length, - itemBuilder: (_, index) => buildItem(index)))); + child: controller.list.isEmpty + ? const Center( + child: Empty(text: '暂无数据'), + ) + : ListView.separated( + separatorBuilder: (context, index) => const Divider( + color: AppTheme.dividerColor, + height: 1, + ), + itemCount: controller.list.length, + itemBuilder: (_, index) => buildItem(index)))); } Widget buildItem(int index) { diff --git a/lib/widgets/core/sk_single_search_more.dart b/lib/widgets/core/sk_single_search_more.dart index 23a6210..561706d 100644 --- a/lib/widgets/core/sk_single_search_more.dart +++ b/lib/widgets/core/sk_single_search_more.dart @@ -125,20 +125,18 @@ class SkSingleSearchMore extends StatelessWidget { controller: controller.refreshController, onLoading: controller.onLoading, onRefresh: controller.onRefresh, - child: controller.refreshController.isLoading - ? const SizedBox() - : controller.list.isEmpty - ? const Center( - child: Empty(text: '暂无数据'), - ) - : ListView.separated( - separatorBuilder: (context, index) => const Divider( - color: AppTheme.dividerColor, - ), - itemCount: controller.list.length, - itemBuilder: (_, index) => itemBuilder != null - ? itemBuilder!(_, index) - : buildItem(index)))); + child: controller.list.isEmpty + ? const Center( + child: Empty(text: '暂无数据'), + ) + : ListView.separated( + separatorBuilder: (context, index) => const Divider( + color: AppTheme.dividerColor, + ), + itemCount: controller.list.length, + itemBuilder: (_, index) => itemBuilder != null + ? itemBuilder!(_, index) + : buildItem(index)))); } Widget buildItem(int index) { diff --git a/lib/widgets/core/sk_tag.dart b/lib/widgets/core/sk_tag.dart new file mode 100644 index 0000000..61dc925 --- /dev/null +++ b/lib/widgets/core/sk_tag.dart @@ -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)), + ), + ); + } +} diff --git a/lib/widgets/core/sk_text_input.dart b/lib/widgets/core/sk_text_input.dart index 62e8233..adaada7 100644 --- a/lib/widgets/core/sk_text_input.dart +++ b/lib/widgets/core/sk_text_input.dart @@ -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? 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,26 +82,33 @@ class _SkTextInputState extends State { 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( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.isRequired) - Text( - "*", - style: TextStyle( - color: Colors.red, fontSize: ScreenAdaper.height(30)), - ), - Text( - widget.labelText, - style: TextStyle(fontSize: ScreenAdaper.height(30)), - ), - ]), + border: widget.border, + floatingLabelBehavior: widget.floatingLabelBehavior, + label: widget.labelText != null + ? Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.isRequired) + Text( + "*", + style: TextStyle( + color: Colors.red, + fontSize: ScreenAdaper.height(30)), + ), + Text( + widget.labelText!, + style: TextStyle(fontSize: ScreenAdaper.height(30)), + ), + ]) + : null, focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: AppTheme.primaryColorLight, width: 2), borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))), diff --git a/lib/widgets/sk_appbar.dart b/lib/widgets/sk_appbar.dart index b05f656..a931dc9 100644 --- a/lib/widgets/sk_appbar.dart +++ b/lib/widgets/sk_appbar.dart @@ -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? 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);