diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 8d14093..99259a5 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -45,5 +45,21 @@
android:name="android.permission.READ_EXTERNAL_STORAGE" />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 2340daf..2d03c92 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -3,7 +3,7 @@
CADisableMinimumFrameDurationOnPhone
-
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
@@ -25,7 +25,7 @@
CFBundleVersion
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
-
+
Launch screen interface file base name
$(LAUNCH_SCREEN_STORYBOARD)
NSCameraUsageDescription
@@ -35,13 +35,18 @@
NSPhotoLibraryUsageDescription
App需要您的同意,才能访问相册
UIApplicationSupportsIndirectInputEvents
-
+
+ LSApplicationQueriesSchemes
+
+ sms
+ tel
+
UILaunchStoryboardName
LaunchScreen.storyboard
UIMainStoryboardFile
Main
UIStatusBarHidden
-
+
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
@@ -56,6 +61,6 @@
UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
-
+
-
+
\ No newline at end of file
diff --git a/lib/apis/index.dart b/lib/apis/index.dart
index 7f8d2b3..e7e4b45 100644
--- a/lib/apis/index.dart
+++ b/lib/apis/index.dart
@@ -22,13 +22,18 @@ class Api {
);
}
-// 获取个人信息
- static Future getUserInfo() {
+// 获取我的个人信息
+ static Future getMyProfile() {
return DioService.dio.get(
Urls.userInfo,
);
}
+// 获取个人信息
+ static Future getUserInfo(int userid) {
+ return DioService.dio.get('${Urls.userInfo}/$userid');
+ }
+
// 分页获取项目列表
static Future> getProjects(Map params) {
return DioService.dio.get(Urls.projects,
diff --git a/lib/app_theme.dart b/lib/app_theme.dart
index 07b7cbd..5413d6f 100644
--- a/lib/app_theme.dart
+++ b/lib/app_theme.dart
@@ -26,7 +26,7 @@ class AppTheme {
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 scaffoldBackgroundColor = Color(0XFFe9f0fd);
static const Color inputFillColor = Color(0xFFf5f8ff);
}
diff --git a/lib/constants/global_url.dart b/lib/constants/global_url.dart
index c9b6561..62704a0 100644
--- a/lib/constants/global_url.dart
+++ b/lib/constants/global_url.dart
@@ -5,7 +5,7 @@ class Urls {
static String deleteAccount = 'user/deleteAccount';
static String saveUserInfo = 'user/saveUserInfo';
static String sysUser = 'system/users';
- static String userInfo = 'account/profile';
+ static String myProfile = 'account/profile';
static String projects = 'project';
static String products = 'product';
static String inventoryInout = 'materials-in-out';
@@ -15,4 +15,5 @@ class Urls {
static String uploadAttachemnt = 'tools/upload';
static String systemParamConfig = 'system/param-config';
static String accountMenus = 'account/menus';
+ static String userInfo = 'system/users';
}
diff --git a/lib/constants/router.dart b/lib/constants/router.dart
index 9a5fc54..ae8675c 100644
--- a/lib/constants/router.dart
+++ b/lib/constants/router.dart
@@ -1,4 +1,6 @@
import 'package:get/get.dart';
+import 'package:sk_base_mobile/models/user_info.model.dart';
+import 'package:sk_base_mobile/screens/hr_manage/components/employee_detail.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';
@@ -14,6 +16,7 @@ class RouteConfig {
static const String inventory = '/inventory';
static const String saleQuotation = '/sale_quotation';
static const String hrManage = '/hr_manage';
+ static const String employeeDetail = '/employee_detail';
static final List getPages = [
GetPage(name: login, page: () => LoginScreen()),
@@ -21,6 +24,7 @@ class RouteConfig {
GetPage(name: userinfo, page: () => UserInfoPage()),
GetPage(name: inventory, page: () => const InventoryPage()),
GetPage(name: saleQuotation, page: () => SaleQuotationPage()),
- GetPage(name: hrManage, page: () => HrManagePage())
+ GetPage(name: hrManage, page: () => HrManagePage()),
+ GetPage(name: employeeDetail, page: () => EmployeeDetail())
];
}
diff --git a/lib/screens/hr_manage/components/dept_picker.dart b/lib/screens/hr_manage/components/dept_picker.dart
new file mode 100644
index 0000000..8189131
--- /dev/null
+++ b/lib/screens/hr_manage/components/dept_picker.dart
@@ -0,0 +1,90 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/util/screen_adaper_util.dart';
+import 'package:sk_base_mobile/widgets/core/sk_cascade_picker.dart';
+import 'package:sk_base_mobile/widgets/gradient_button.dart';
+
+class DeptPicker extends StatelessWidget {
+ DeptPicker({super.key});
+
+ final _cascadeController = Get.put(CascadeController());
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ decoration: BoxDecoration(color: AppTheme.nearlyWhite),
+ height: ScreenAdaper.height(400),
+ child: Column(
+ children: [
+ GradientButton(
+ buttonText: '确定',
+ onPressed: () {
+ if (_cascadeController.isCompleted()) {
+ // 已选中的titles
+ List selectedTitles =
+ _cascadeController.selectedTitles;
+ print("已选中的titles: $selectedTitles");
+ // 已选中的序号
+ List selectedIndexes =
+ _cascadeController.selectedIndexes;
+ print("已选中的序号:$selectedIndexes");
+
+ Item item = _cascadeController
+ .items[selectedIndexes[0]]
+ .children![selectedIndexes[1]]
+ .children![selectedIndexes[2]];
+ print("已选择item( ${item.name} )");
+ }
+ },
+ ),
+ SizedBox(
+ height: 10,
+ ),
+ Expanded(
+ child: CascadePicker(
+ initialPageData:
+ _cascadeController.items.map((e) => e.name!).toList(),
+ nextPageData: (pageCallback, currentPage, selectIndex) async {
+ print("当前选择: 第$currentPage页, 第$selectIndex项");
+ if (currentPage == 1) {
+ // 在第一页选中,返回第二页列表数据
+ List? nextPageData = _cascadeController
+ .items[selectIndex].children
+ ?.map((e) => e.name!)
+ .toList();
+ if (nextPageData != null) pageCallback(nextPageData);
+ } else if (currentPage == 2) {
+ // 在第二页选中,返回第二页列表数据
+ // 先获取已选中的序号
+ List selectedIndexes =
+ _cascadeController.selectedIndexes;
+ // 根据已选中的序号在items中获取下一级页面的列表数据
+ List? nextPageData = _cascadeController
+ .items[selectedIndexes[0]].children?[selectIndex].children
+ ?.map((e) => e.name!)
+ .toList();
+ if (nextPageData != null) pageCallback(nextPageData);
+ }
+ },
+ controller: _cascadeController,
+ maxPageNum: 3,
+ tabTitleStyle: TextStyle(
+ fontSize: ScreenAdaper.height(26), color: Colors.black),
+ itemTitleStyle: TextStyle(
+ fontSize: ScreenAdaper.height(26), color: Colors.black),
+ selectedIcon:
+ Icon(Icons.check, color: AppTheme.primaryColorLight),
+ ))
+ ],
+ ));
+ }
+}
+
+class Item {
+ String? name;
+ String? code;
+ String? fatherCode;
+ String? remark;
+ List- ? children;
+}
diff --git a/lib/screens/hr_manage/components/edit_userinfo.dart b/lib/screens/hr_manage/components/edit_userinfo.dart
new file mode 100644
index 0000000..10b0fa3
--- /dev/null
+++ b/lib/screens/hr_manage/components/edit_userinfo.dart
@@ -0,0 +1,285 @@
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:get/get.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/screens/hr_manage/components/dept_picker.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/core/sk_cascade_picker.dart';
+import 'package:sk_base_mobile/widgets/core/sk_dialog_header.dart';
+import 'package:sk_base_mobile/widgets/core/sk_text_input.dart';
+import 'package:sk_base_mobile/widgets/gradient_button.dart';
+
+/// 编辑用户信息
+class EditUserInfo extends StatelessWidget {
+ final int userId;
+ final EditUserInfoController controller;
+ EditUserInfo({super.key, required this.userId})
+ : controller = Get.put(EditUserInfoController(userId));
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: () {
+ FocusScope.of(context).requestFocus(FocusNode());
+ },
+ child: buildBody(),
+ );
+ }
+
+ Widget buildBody() {
+ return Column(
+ children: [
+ const SkDialogHeader(title: '编辑员工信息'),
+ Expanded(
+ child: SingleChildScrollView(
+ child: Container(
+ padding: EdgeInsets.symmetric(
+ horizontal: ScreenAdaper.height(15),
+ vertical: ScreenAdaper.height(15)),
+ child: Column(children: [
+ buildAvatar(),
+ SizedBox(
+ height: ScreenAdaper.height(defaultPadding),
+ ),
+ SkTextInput(
+ isDense: true,
+ textController: controller.nameEditController,
+ labelText: '姓名',
+ ),
+ SizedBox(
+ height: ScreenAdaper.height(defaultPadding),
+ ),
+ SkTextInput(
+ isDense: true,
+ keyboardType: TextInputType.none,
+ textController: TextEditingController(),
+ labelText: '所属部门',
+ onTap: (_) async {
+ Get.bottomSheet(DeptPicker())
+ .then((value) => Get.delete());
+ },
+ ),
+ SizedBox(
+ height: ScreenAdaper.height(defaultPadding),
+ ),
+ SkTextInput(
+ isDense: true,
+ keyboardType: TextInputType.none,
+ textController: TextEditingController(),
+ labelText: '角色',
+ onTap: (_) async {
+ Get.bottomSheet(Container(
+ height: ScreenAdaper.height(400),
+ decoration: BoxDecoration(color: AppTheme.nearlyWhite),
+ child: Column(children: [
+ Container(
+ padding: EdgeInsets.symmetric(
+ vertical: ScreenAdaper.height(20),
+ horizontal: ScreenAdaper.width(20)),
+ child: Row(
+ children: [
+ Text(
+ '取消',
+ style: TextStyle(
+ fontSize: ScreenAdaper.height(30)),
+ ),
+ Spacer(),
+ Text(
+ '确定',
+ style: TextStyle(
+ fontSize: ScreenAdaper.height(30),
+ color: AppTheme.primaryColor),
+ )
+ ],
+ ),
+ ),
+ Expanded(
+ child: SingleChildScrollView(
+ child: ListBody(
+ children: [
+ '角色1',
+ '角色2',
+ '角色3',
+ '角色4',
+ '角色5',
+ '角色6',
+ '角色7',
+ '角色8',
+ '角色9'
+ ].map((String text) {
+ return Obx(
+ () => CheckboxListTile(
+ contentPadding: EdgeInsets.symmetric(
+ vertical: ScreenAdaper.height(0),
+ horizontal: ScreenAdaper.width(20)),
+ title: Text(text),
+ value:
+ controller.selectedDepts.contains(text),
+ onChanged: (bool? value) {
+ if (value == true) {
+ controller.selectedDepts.add(text);
+ } else {
+ controller.selectedDepts.remove(text);
+ }
+ },
+ ),
+ );
+ }).toList(),
+ ),
+ ))
+ ]),
+ ));
+ }),
+ SizedBox(
+ height: ScreenAdaper.height(defaultPadding),
+ ),
+ SkTextInput(
+ isDense: true,
+ textController: TextEditingController(),
+ labelText: '登录用户名',
+ ),
+ SizedBox(
+ height: ScreenAdaper.height(defaultPadding),
+ ),
+ SkTextInput(
+ isDense: true,
+ textController: TextEditingController(),
+ labelText: '手机号',
+ )
+ ]),
+ ),
+ )),
+ const GradientButton(
+ borderRadius: BorderRadius.zero,
+ buttonText: '提交',
+ )
+ // Container(
+ // decoration: BoxDecoration(color: AppTheme.primaryColor),
+ // height: 100,
+ // )
+ ],
+ );
+ }
+
+ Widget buildAvatar() {
+ return Column(
+ children: [
+ SizedBox(
+ height: ScreenAdaper.height(10),
+ ),
+ buildImageUploader(),
+ ],
+ );
+ }
+
+ Widget builderImagePreview(String path, String type) {
+ return Stack(
+ children: [
+ Container(
+ margin: EdgeInsets.symmetric(horizontal: ScreenAdaper.width(5)),
+ width: ScreenAdaper.width(180),
+ height: ScreenAdaper.width(180),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(20),
+ border: Border.all(width: 1.0, color: AppTheme.dividerColor),
+ // color: AppTheme.primaryColor,
+ ),
+ child: Center(
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ image: DecorationImage(
+ fit: BoxFit.cover,
+ image: FileImage(File(path)),
+ ),
+ ),
+ ),
+ ),
+ ),
+ Positioned(
+ top: ScreenAdaper.height(5),
+ right: ScreenAdaper.width(5),
+ child: GestureDetector(
+ onTap: () {
+ if (type == 'agent') {
+ // controller.uploadAgentImgFilesPath.remove(path);
+ } else {
+ // controller.uploadProductImgFilesPath.remove(path);
+ }
+ },
+ child: Icon(
+ Icons.close,
+ shadows: const [
+ Shadow(
+ color: Colors.black,
+ offset: Offset(1, 1),
+ blurRadius: 3,
+ ),
+ Shadow(
+ color: Colors.black,
+ offset: Offset(-1, -1),
+ blurRadius: 3,
+ ),
+ ],
+ size: ScreenAdaper.height(40),
+ color: AppTheme.nearlyWhite,
+ ),
+ ))
+ ],
+ );
+ }
+
+ // 上传照片控制器
+ Widget buildImageUploader() {
+ return GestureDetector(
+ onTap: () {
+ // controller.photoPicker(type);
+ },
+ child: Container(
+ margin: EdgeInsets.symmetric(horizontal: ScreenAdaper.width(5)),
+ width: ScreenAdaper.width(180),
+ height: ScreenAdaper.width(180),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(100),
+ border: Border.all(width: 1.0, color: AppTheme.dividerColor),
+ // color: AppTheme.primaryColor,
+ ),
+ child: Center(
+ child: Icon(
+ Icons.add_a_photo_rounded,
+ size: ScreenAdaper.height(60),
+ color: AppTheme.primaryColor,
+ ))));
+ }
+}
+
+class EditUserInfoController extends GetxController {
+ int userId;
+ EditUserInfoController(this.userId);
+ final nameEditController = TextEditingController();
+ final userInfo = Rxn();
+ RxList selectedDepts = RxList([]);
+
+ @override
+ onReady() {
+ getUserInfo();
+ super.onReady();
+ }
+
+ Future getUserInfo() async {
+ try {
+ final response = await Api.getUserInfo(userId);
+ if (response.data != null) {
+ userInfo.value = UserInfoModel.fromJson(response.data);
+ nameEditController.text = userInfo.value?.nickname ?? '';
+ }
+ } catch (e) {
+ SnackBarUtil().error('$e');
+ } finally {}
+ }
+}
diff --git a/lib/screens/hr_manage/components/employee_detail.dart b/lib/screens/hr_manage/components/employee_detail.dart
new file mode 100644
index 0000000..6a6c92e
--- /dev/null
+++ b/lib/screens/hr_manage/components/employee_detail.dart
@@ -0,0 +1,214 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
+import 'package:get/get.dart';
+import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/config.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/screens/inventory_inout/components/responsive.dart';
+import 'package:sk_base_mobile/util/screen_adaper_util.dart';
+import 'package:sk_base_mobile/widgets/core/sk_tag.dart';
+import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
+import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
+
+class EmployeeDetail extends StatelessWidget {
+ final _controller = Get.put(EmployeeDetailController());
+ EmployeeDetail({super.key});
+ final userInfo = Get.arguments as UserInfoModel;
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: const SkAppbar(
+ backgroundColor: AppTheme.white,
+ iconAndTextColor: AppTheme.black,
+ title: '员工详情',
+ ),
+ body: buildBody(),
+ );
+ }
+
+ Widget buildBody() {
+ return Column(
+ children: [
+ buildTopInfo(),
+ Expanded(child: buildContent()),
+ ],
+ );
+ }
+
+ Widget buildTopInfo() {
+ return Container(
+ padding: EdgeInsets.symmetric(
+ vertical: ScreenAdaper.height(defaultPadding),
+ horizontal: ScreenAdaper.width(defaultPadding * 2)),
+ color: AppTheme.white,
+ child: Row(
+ children: [
+ ClipRRect(
+ borderRadius: const BorderRadius.all(Radius.circular(30)),
+ child: FadeInCacheImage(
+ height: ScreenAdaper.height(80),
+ width: ScreenAdaper.height(80),
+ url: '${GloablConfig.OSS_URL}${userInfo.avatar}'),
+ ),
+ SizedBox(
+ width: ScreenAdaper.width(defaultPadding),
+ ),
+ // 中间信息
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text('${userInfo.nickname}',
+ style: TextStyle(
+ fontSize: ScreenAdaper.height(30),
+ fontWeight: FontWeight.w600)),
+
+ SizedBox(
+ height: ScreenAdaper.height(5),
+ ), // role
+ Text('${userInfo.dept?.name}',
+ style: TextStyle(color: AppTheme.grey)),
+ SizedBox(
+ height: ScreenAdaper.height(5),
+ ),
+ Wrap(
+ spacing: ScreenAdaper.height(5),
+ runSpacing: ScreenAdaper.height(5),
+ children: [
+ ...userInfo.roles.map((e) => SkTag(
+ text: '${e.name}', color: AppTheme.primaryColorLight))
+ ],
+ ),
+ ],
+ )),
+ ],
+ ),
+ );
+ }
+
+ Widget buildContent() {
+ return Container(
+ margin: EdgeInsets.all(ScreenAdaper.height(defaultPadding)),
+ decoration: const BoxDecoration(color: AppTheme.white),
+ child: DefaultTabController(
+ length: 4,
+ initialIndex: _controller.selectedTabIndex.value,
+ child: Column(
+ children: [
+ _buildTabBar(),
+ Expanded(
+ child: _buildTabView(),
+ )
+ ],
+ )),
+ );
+ }
+
+ Widget _buildTabBar() {
+ return TabBar(
+ onTap: (index) {
+ _controller.selectedTabIndex.value = index;
+ },
+ labelStyle: TextStyle(
+ fontSize: ScreenAdaper.height(25), fontWeight: FontWeight.w600),
+ labelPadding: EdgeInsets.zero,
+ labelColor: AppTheme.primaryColorLight,
+ // unselectedLabelColor: AppTheme.grey,
+ indicator: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ width: ScreenAdaper.height(5),
+ color: AppTheme.primaryColorLight,
+ )),
+ ),
+ tabs: const [
+ Tab(
+ text: '基本信息',
+ ),
+ Tab(
+ text: '考勤记录',
+ ),
+ Tab(
+ text: '工作安排',
+ ),
+ Tab(
+ text: '相关文件',
+ ),
+ ],
+ );
+ }
+
+ Widget _buildTabView() {
+ return TabBarView(
+ children: [
+ Responsive(
+ tablet: buildBaseInfo(
+ columnNum: 3,
+ ),
+ largeTablet: buildBaseInfo(
+ columnNum: 4,
+ ),
+ mobile: buildBaseInfo(
+ columnNum: 2,
+ )),
+ Container(
+ child: Text('考勤记录'),
+ ),
+ Container(
+ child: Text('请假记录'),
+ ),
+ Container(
+ child: Text('加班记录'),
+ ),
+ ],
+ );
+ }
+
+ //基本信息
+ Widget buildBaseInfo({
+ required int columnNum,
+ }) {
+ return Container(
+ padding: EdgeInsets.symmetric(
+ vertical: ScreenAdaper.height(defaultPadding),
+ horizontal: ScreenAdaper.height(defaultPadding)),
+ child: MasonryGridView.count(
+ crossAxisCount: columnNum,
+ itemCount: 7,
+ mainAxisSpacing: ScreenAdaper.height(50),
+ itemBuilder: (context, index) {
+ return buildBaseInfoItem(label: '姓名', value: '张三');
+ },
+ ));
+ }
+
+ Widget buildBaseInfoItem({String? label, String? value}) {
+ return Container(
+ // decoration: BoxDecoration(color: Colors.red),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ '$label',
+ style: TextStyle(
+ fontSize: ScreenAdaper.height(26),
+ color: AppTheme.grey,
+ fontWeight: FontWeight.w600),
+ ),
+ SizedBox(
+ height: ScreenAdaper.height(10),
+ ),
+ Text(
+ '$value',
+ style: TextStyle(fontSize: ScreenAdaper.height(26)),
+ )
+ ],
+ ),
+ );
+ }
+}
+
+class EmployeeDetailController extends GetxController {
+ final selectedTabIndex = 0.obs;
+}
diff --git a/lib/screens/hr_manage/hr_manage.dart b/lib/screens/hr_manage/hr_manage.dart
index d10871e..49d76d3 100644
--- a/lib/screens/hr_manage/hr_manage.dart
+++ b/lib/screens/hr_manage/hr_manage.dart
@@ -3,16 +3,26 @@ 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/config.dart';
import 'package:sk_base_mobile/constants/bg_color.dart';
+import 'package:sk_base_mobile/constants/constants.dart';
import 'package:sk_base_mobile/models/user_info.model.dart';
+import 'package:sk_base_mobile/screens/hr_manage/components/edit_userinfo.dart';
+import 'package:sk_base_mobile/util/common.util.dart';
import 'package:sk_base_mobile/util/date.util.dart';
import 'package:sk_base_mobile/util/debouncer.dart';
+import 'package:sk_base_mobile/util/device.util.dart';
+import 'package:sk_base_mobile/util/media_util.dart';
+import 'package:sk_base_mobile/util/modal.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
+import 'package:sk_base_mobile/util/snack_bar.util.dart';
import 'package:sk_base_mobile/widgets/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';
+import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
+import 'package:sk_base_mobile/widgets/loading_indicator.dart';
+import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
class HrManagePage extends StatelessWidget {
final controller = Get.put(HrManageController());
@@ -20,14 +30,21 @@ class HrManagePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ // return Column(
+ // children: [
+ // Expanded(
+ // child: Material(
+ // child: EditUserInfo(),
+ // ))
+ // ],
+ // );
return GestureDetector(
onTap: () {
// 取消焦点
FocusScope.of(context).requestFocus(FocusNode());
},
child: Scaffold(
- backgroundColor: Color(0xFFe9f0fd),
- appBar: SkAppbar(
+ appBar: const SkAppbar(
backgroundColor: AppTheme.nearlyWhite,
iconAndTextColor: AppTheme.black,
title: '人事管理',
@@ -43,25 +60,30 @@ class HrManagePage extends StatelessWidget {
Expanded(
child: Container(
margin: EdgeInsets.symmetric(
- horizontal: ScreenAdaper.height(defaultPadding),
- vertical: ScreenAdaper.height(defaultPadding)),
+ horizontal: 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,
- )),
+ () => controller.loading.value
+ ? const LoadingIndicator(common: true)
+ : 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(
+ padding: EdgeInsets.symmetric(
+ vertical:
+ ScreenAdaper.height(defaultPadding)),
+ itemBuilder: (context, index) {
+ return buildUserCard(index);
+ },
+ itemCount: controller.list.length,
+ )),
)),
)
],
@@ -75,11 +97,8 @@ class HrManagePage extends StatelessWidget {
}, 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: [
+ padding: EdgeInsets.all(ScreenAdaper.width(20)),
+ child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
Expanded(
child: SizedBox(
height: ScreenAdaper.height(70),
@@ -132,98 +151,124 @@ class HrManagePage extends StatelessWidget {
}
Widget buildUserCard(int index) {
- return Container(
- padding: EdgeInsets.symmetric(
- horizontal: ScreenAdaper.height(defaultPadding),
- vertical: ScreenAdaper.height(defaultPadding)),
+ return SkInk(
+ onTap: () {
+ Get.toNamed(RouteConfig.employeeDetail,
+ arguments: controller.list[index]);
+ },
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)),
+ borderRadius: BorderRadius.circular(ScreenAdaper.sp(15)),
+ child: Container(
+ padding: EdgeInsets.symmetric(
+ horizontal: ScreenAdaper.height(defaultPadding),
+ vertical: ScreenAdaper.height(defaultPadding)),
+ child: Column(
+ children: [
+ Row(
+ children: [
+ // 头像
+ ClipRRect(
+ borderRadius: const BorderRadius.all(Radius.circular(30)),
+ child: FadeInCacheImage(
+ height: ScreenAdaper.height(80),
+ width: ScreenAdaper.height(80),
+ url:
+ MediaUtil.getMediaUrl(controller.list[index].avatar)),
+ ),
+ 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))
- ],
- )),
- ],
- )),
+ 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),
- ),
+ /// 右侧action
+ /// 电话
+ buildActionButton(
+ onTap: () {
+ if (controller.list[index].phone == null) {
+ SnackBarUtil().info('该员工没有录入手机号');
+ return;
+ }
+ DeviceUtil.callPhone(controller.list[index].phone!);
+ },
+ icon: Icons.phone,
+ ),
+ SizedBox(
+ width: ScreenAdaper.height(defaultPadding),
+ ),
- /// 邮件
- buildActionButton(
- onTap: () {},
- icon: Icons.email_outlined,
- ),
- SizedBox(
- width: ScreenAdaper.height(defaultPadding),
- ),
+ /// 邮件
+ buildActionButton(
+ onTap: () {
+ if (controller.list[index].email == null) {
+ SnackBarUtil().info('该员工没有录入邮箱');
+ return;
+ }
+ DeviceUtil.sendEmail(controller.list[index].email!);
+ },
+ 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,
- )
- ],
- )
- ],
+ /// 编辑
+ buildActionButton(
+ onTap: () {
+ ModalUtil.showGeneralDialog(
+ content: EditUserInfo(
+ userId: controller.list[index].id!,
+ )).then((value) => Get.delete());
+
+ // Get.toNamed(RouteConfig.employeeDetail,
+ // arguments: controller.list[index]);
+ },
+ icon: Icons.edit,
+ )
+ ],
+ ),
+ const Divider(),
+ Row(
+ children: [
+ Text(
+ '工龄: ${SkDateUtil.howLongAgo(controller.list[index].createdAt!)}'),
+ const Spacer(),
+ const SkTag(
+ text: '在职',
+ color: Colors.green,
+ )
+ ],
+ )
+ ],
+ ),
),
);
}
@@ -233,14 +278,14 @@ class HrManagePage extends StatelessWidget {
required IconData icon,
}) {
return SkInk(
- onTap: () {},
+ onTap: onTap,
border: Border.all(color: AppTheme.grey.withOpacity(0.8)),
- borderRadius: BorderRadius.circular(ScreenAdaper.sp(30)),
+ borderRadius: BorderRadius.circular(ScreenAdaper.sp(40)),
child: Container(
padding: EdgeInsets.all(ScreenAdaper.height(5)),
child: Icon(
icon,
- size: ScreenAdaper.height(35),
+ size: ScreenAdaper.height(40),
),
),
);
@@ -251,10 +296,27 @@ class HrManageController extends GetxController {
RxList list = RxList([]);
RxString searchKey = ''.obs;
final searchBarTextConroller = TextEditingController();
- RefreshController refreshController = RefreshController(initialRefresh: true);
+ RefreshController refreshController =
+ RefreshController(initialRefresh: false);
int page = 1;
int limit = 15;
int total = 0;
+ RxBool loading = false.obs;
+ @override
+ onReady() {
+ super.onReady();
+ initData();
+ }
+
+ initData() async {
+ loading.value = true;
+ try {
+ await getData(isRefresh: true);
+ } finally {
+ loading.value = false;
+ }
+ }
+
Future
> getData({bool isRefresh = false}) async {
if (isRefresh == true) {
page = 1;
diff --git a/lib/screens/inventory/inventory.dart b/lib/screens/inventory/inventory.dart
index d5aca44..1845824 100644
--- a/lib/screens/inventory/inventory.dart
+++ b/lib/screens/inventory/inventory.dart
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/components/inventory_search.dart';
-import 'package:sk_base_mobile/widgets/sk_appbar.dart';
+import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
class InventoryPage extends StatelessWidget {
final bool isPage;
diff --git a/lib/screens/inventory_inout/components/inventory_inout_info.dart b/lib/screens/inventory_inout/components/inventory_inout_info.dart
index f47d6fc..2af6374 100644
--- a/lib/screens/inventory_inout/components/inventory_inout_info.dart
+++ b/lib/screens/inventory_inout/components/inventory_inout_info.dart
@@ -11,6 +11,7 @@ import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
import 'package:sk_base_mobile/widgets/image_preview.dart';
import 'package:sk_base_mobile/widgets/loading_indicator.dart';
+import 'package:sk_base_mobile/widgets/core/sk_dialog_header.dart';
class InventoryInoutInfo extends StatelessWidget {
final int inventoryInoutId;
@@ -22,23 +23,7 @@ class InventoryInoutInfo extends StatelessWidget {
Widget build(BuildContext context) {
return SizedBox(
child: Column(children: [
- Container(
- padding: EdgeInsets.symmetric(
- horizontal: ScreenAdaper.width(10),
- vertical: ScreenAdaper.height(15)),
- decoration: const BoxDecoration(color: AppTheme.primaryColor),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text(
- '出入库详情',
- style: TextStyle(
- fontSize: ScreenAdaper.height(25),
- color: AppTheme.nearlyWhite),
- ),
- ],
- ),
- ),
+ const SkDialogHeader(title: '出入库详情'),
Expanded(
child: Obx(() => controller.inventoryInoutInfo.value == null
? const Center(child: LoadingIndicator(common: true))
diff --git a/lib/screens/mine/mine.controller.dart b/lib/screens/mine/mine.controller.dart
index 438920e..98b62bf 100644
--- a/lib/screens/mine/mine.controller.dart
+++ b/lib/screens/mine/mine.controller.dart
@@ -15,7 +15,7 @@ class MineController extends GetxController {
@override
void onInit() {
- // AuthStore.to.getUserInfo();
+ // AuthStore.to.getMyProfile();
super.onInit();
}
diff --git a/lib/screens/new_inventory_inout/new_inventory_inout.dart b/lib/screens/new_inventory_inout/new_inventory_inout.dart
index 0110f9c..bbd1050 100644
--- a/lib/screens/new_inventory_inout/new_inventory_inout.dart
+++ b/lib/screens/new_inventory_inout/new_inventory_inout.dart
@@ -19,7 +19,7 @@ import 'package:sk_base_mobile/widgets/core/sk_date_picker.dart';
import 'package:sk_base_mobile/widgets/core/sk_text_input.dart';
import 'package:sk_base_mobile/widgets/gradient_button.dart';
import 'package:sk_base_mobile/screens/new_inventory_inout/new_inventory_inout_controller.dart';
-import 'package:sk_base_mobile/widgets/sk_appbar.dart';
+import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
class NewInventoryInout extends StatelessWidget {
final NewInventoryInoutController controller;
diff --git a/lib/screens/sale_quotation/components/sale_quotation_drawer.dart b/lib/screens/sale_quotation/components/sale_quotation_drawer.dart
index 53e599f..c70cd9e 100644
--- a/lib/screens/sale_quotation/components/sale_quotation_drawer.dart
+++ b/lib/screens/sale_quotation/components/sale_quotation_drawer.dart
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/widgets/gradient_button.dart';
-import 'package:sk_base_mobile/widgets/sk_appbar.dart';
+import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
class SaleQuotationEndDrawer extends StatelessWidget {
const SaleQuotationEndDrawer({super.key});
diff --git a/lib/screens/sale_quotation/sale_quotation.dart b/lib/screens/sale_quotation/sale_quotation.dart
index 1d6013e..b396bc4 100644
--- a/lib/screens/sale_quotation/sale_quotation.dart
+++ b/lib/screens/sale_quotation/sale_quotation.dart
@@ -1,5 +1,3 @@
-import 'dart:ffi';
-
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
@@ -14,10 +12,9 @@ import 'package:sk_base_mobile/util/snack_bar.util.dart';
import 'package:sk_base_mobile/widgets/core/sk_number_input.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';
+import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
import 'package:math_expressions/math_expressions.dart';
-import 'dart:math' as math;
class SaleQuotationPage extends StatelessWidget {
SaleQuotationPage({super.key});
diff --git a/lib/screens/workbench/workbench.dart b/lib/screens/workbench/workbench.dart
index 9411965..ca97c52 100644
--- a/lib/screens/workbench/workbench.dart
+++ b/lib/screens/workbench/workbench.dart
@@ -7,7 +7,7 @@ import 'package:sk_base_mobile/constants/router.dart';
import 'package:sk_base_mobile/screens/workbench/workbench_controller.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
import 'package:sk_base_mobile/util/snack_bar.util.dart';
-import 'package:sk_base_mobile/widgets/sk_appbar.dart';
+import 'package:sk_base_mobile/widgets/core/sk_appbar.dart';
class WorkBenchPage extends StatelessWidget {
WorkBenchPage({super.key});
diff --git a/lib/screens/workbench/workbench_controller.dart b/lib/screens/workbench/workbench_controller.dart
index f0562c9..1072994 100644
--- a/lib/screens/workbench/workbench_controller.dart
+++ b/lib/screens/workbench/workbench_controller.dart
@@ -1,4 +1,3 @@
-import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sk_base_mobile/models/workbench.model.dart';
diff --git a/lib/store/auth.store.dart b/lib/store/auth.store.dart
index 812cb7c..ddda41b 100644
--- a/lib/store/auth.store.dart
+++ b/lib/store/auth.store.dart
@@ -26,7 +26,7 @@ class AuthStore extends GetxService {
if (token != null) {
if (preUserInfo != null) {
- await getUserInfo();
+ await getMyProfile();
LoggerUtil().info('[Store-Auth] userId: ${userInfo.value.id}');
}
}
@@ -87,7 +87,7 @@ class AuthStore extends GetxService {
await StorageService.to
.setString(CacheKeys.token, auth.token!, isWithUser: false);
}
- await getUserInfo();
+ await getMyProfile();
await getCommonInfo();
Get.offNamed(RouteConfig.home);
}
@@ -105,9 +105,9 @@ class AuthStore extends GetxService {
.setString(CacheKeys.userInfo, jsonEncode(newInfo), isWithUser: false);
}
- Future getUserInfo() async {
+ Future getMyProfile() async {
try {
- final response = await Api.getUserInfo();
+ final response = await Api.getMyProfile();
if (response.data != null) {
UserInfoModel userInfo = UserInfoModel.fromJson(response.data);
await updateUserInfoState(userInfo);
@@ -122,7 +122,7 @@ class AuthStore extends GetxService {
try {
final res = await Api.saveUserInfo(data);
if (res.data != null) {
- await getUserInfo();
+ await getMyProfile();
SnackBarUtil().success('Save successfully.');
}
} catch (e) {
diff --git a/lib/util/device.util.dart b/lib/util/device.util.dart
index 5022370..4fdb858 100644
--- a/lib/util/device.util.dart
+++ b/lib/util/device.util.dart
@@ -1,7 +1,30 @@
import 'dart:io';
import 'package:package_info/package_info.dart';
+import 'package:url_launcher/url_launcher.dart';
class DeviceUtil {
+ ///发邮件
+ static void sendEmail(String email) {
+ if (email.isNotEmpty) {
+ String url = 'mailto:$email';
+ launchUrl(Uri.parse(url));
+ }
+ }
+
+ ///打电话
+ static void callPhone(String phone) {
+ if (phone.isNotEmpty) {
+ String url = 'tel:$phone';
+ if (Platform.isIOS) {
+ url = 'tel://$phone';
+ }
+ if (Platform.isAndroid) {
+ url = 'tel:$phone';
+ }
+ launchUrl(Uri.parse(url));
+ }
+ }
+
/// 获取当前版本
static Future getAppVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
diff --git a/lib/util/media_util.dart b/lib/util/media_util.dart
index 0c19924..5128e2e 100644
--- a/lib/util/media_util.dart
+++ b/lib/util/media_util.dart
@@ -5,10 +5,19 @@ import 'package:dio/dio.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:sk_base_mobile/apis/index.dart';
import 'package:image_picker/image_picker.dart';
+import 'package:sk_base_mobile/config.dart';
import 'package:sk_base_mobile/models/upload_result.model.dart';
import 'package:sk_base_mobile/services/service.dart';
class MediaUtil {
+ static String? getMediaUrl(String? url) {
+ if ((url ?? '').isEmpty) {
+ return null;
+ } else {
+ return '${GloablConfig.OSS_URL}$url';
+ }
+ }
+
//拍照
Future getImageFromCamera(
{double? maxWidth, bool isFront = false}) async {
@@ -52,6 +61,15 @@ class MediaUtil {
return null;
}
+ // 保存到相册
+ Future saveImageToPhotoLib({required String imgUrl, String? filename}) async {
+ var response = await DioService.dio
+ .get(imgUrl, options: Options(responseType: ResponseType.bytes));
+ final result = await ImageGallerySaver.saveImage(
+ Uint8List.fromList(response.data),
+ quality: 60);
+ return result;
+ }
// // 上传一张照片
// Future uploadMediaWithProgress(
// File imgfile, Function onProgress) async {
@@ -76,16 +94,6 @@ class MediaUtil {
// }
// }
- // 保存到相册
- Future saveImageToPhotoLib({required String imgUrl, String? filename}) async {
- var response = await DioService.dio
- .get(imgUrl, options: Options(responseType: ResponseType.bytes));
- final result = await ImageGallerySaver.saveImage(
- Uint8List.fromList(response.data),
- quality: 60);
- return result;
- }
-
// ///选取视频
// Future getVideoFromGallery() async {
// if (AppInfoModel().isCameraing) {
diff --git a/lib/util/modal.util.dart b/lib/util/modal.util.dart
index 768e5ae..4db115b 100644
--- a/lib/util/modal.util.dart
+++ b/lib/util/modal.util.dart
@@ -167,7 +167,7 @@ class ModalUtil {
double? height,
Offset? offset}) {
return Get.generalDialog(
- barrierLabel: "productPicker",
+ barrierLabel: "generalDialog",
barrierDismissible: true,
transitionDuration: const Duration(milliseconds: 400),
pageBuilder: (_, __, ___) {
diff --git a/lib/util/snack_bar.util.dart b/lib/util/snack_bar.util.dart
index 689d00f..1b958bb 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: 1),
+ duration: const Duration(seconds: 2),
dismissDirection: DismissDirection.horizontal,
forwardAnimationCurve: Curves.fastLinearToSlowEaseIn,
reverseAnimationCurve: Curves.linearToEaseOut);
diff --git a/lib/widgets/sk_appbar.dart b/lib/widgets/core/sk_appbar.dart
similarity index 96%
rename from lib/widgets/sk_appbar.dart
rename to lib/widgets/core/sk_appbar.dart
index a931dc9..380f95f 100644
--- a/lib/widgets/sk_appbar.dart
+++ b/lib/widgets/core/sk_appbar.dart
@@ -1,5 +1,4 @@
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 {
diff --git a/lib/widgets/core/sk_cascade_picker.dart b/lib/widgets/core/sk_cascade_picker.dart
new file mode 100644
index 0000000..d7f30a7
--- /dev/null
+++ b/lib/widgets/core/sk_cascade_picker.dart
@@ -0,0 +1,468 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/screens/hr_manage/components/dept_picker.dart';
+
+/// 级联选择器
+/// 使用示例:
+/// ```dart
+/// CascadePicker的page是ListView,没有约束的情况下它的高度是无限的,
+/// 因此需要约束高度。
+///
+/// final _cascadeController = CascadeController();
+///
+/// initialPageData: 第一页的数据
+/// nextPageData: 下一页的数据,点击当前页的选择项后调用该方法加载下一页
+/// - pageCallback: 用于传递下一页的数据给CascadePicker
+/// - currentPage: 当前是第几页
+/// - selectIndex: 当前选中第几项
+/// controller: 控制器,用于获取已选择的数据
+/// maxPageNum: 最大页数
+/// selectedIcon: 已选中选项前面的图标,flutter package不能放本地资源文件,因此需要从外部传入,图标在images文件夹下面
+///
+/// Expand(
+/// child: CascadePicker(
+/// initialPageData: ['a', 'b', 'c', 'd'],
+/// nextPageData: (pageCallback, currentPage, selectIndex) async {
+/// pageCallback(['one', 'two', 'three'])
+/// },
+/// controller: _cascadeController,
+/// maxPageNum: 4,
+/// selectedIcon: Image.asset("images/ic_select_mark.png", width: 10, height: 10, color: Colors.redAccent,),
+/// )
+///
+/// InkBox(
+/// child: Container(...)
+/// onTap: () {
+/// /// 判断是否完成选择
+/// if (_cascadeController.isCompleted()) {
+/// List selectedTitles = _cascadeController.selectedTitles;
+/// List selectedIndexes = _cascadeController.selectedIndexes;
+/// }
+/// }
+/// )
+/// ```
+
+/// pageData: 下一页的数据
+/// currentPage: 当前是第几页,
+/// selectIndex: 当前页选中第几项
+typedef void NextPageCallback(
+ Function(List) pageData, int currentPage, int selectIndex);
+
+class CascadePicker extends StatefulWidget {
+ final List initialPageData;
+ final NextPageCallback nextPageData;
+ final int maxPageNum;
+ final CascadeController controller;
+ final Color tabColor;
+ final double tabHeight;
+ final TextStyle tabTitleStyle;
+ final double itemHeight;
+ final TextStyle itemTitleStyle;
+ final Color itemColor;
+ final Color activeColor;
+ final Widget? selectedIcon;
+
+ CascadePicker(
+ {required this.initialPageData,
+ required this.nextPageData,
+ this.maxPageNum = 3,
+ required this.controller,
+ this.tabHeight = 40,
+ this.activeColor = AppTheme.primaryColor,
+ this.tabColor = Colors.white,
+ this.tabTitleStyle = const TextStyle(color: Colors.black, fontSize: 14),
+ this.itemHeight = 40,
+ this.itemColor = Colors.white,
+ this.itemTitleStyle = const TextStyle(color: Colors.black, fontSize: 14),
+ this.selectedIcon});
+
+ @override
+ _CascadePickerState createState() => _CascadePickerState(this.controller);
+}
+
+class _CascadePickerState extends State
+ with SingleTickerProviderStateMixin {
+ static String _newTabName = "请选择";
+
+ final CascadeController _cascadeController;
+
+ _CascadePickerState(this._cascadeController) {
+ _cascadeController._setState(this);
+ }
+
+ late final AnimationController _controller;
+ late final CurvedAnimation _curvedAnimation;
+ Animation? _sliderAnimation;
+ final _sliderFixMargin = ValueNotifier(0.0);
+ double _sliderWidth = 20;
+
+ PageController _pageController = PageController(initialPage: 0);
+
+ GlobalKey _sliderKey = GlobalKey();
+ List _tabKeys = [];
+
+ /// 选择器数据集合
+ List> _pagesData = [];
+
+ /// 已选择的title集合
+ List _selectedTabs = [_newTabName];
+
+ /// 已选择的item index集合
+ List _selectedIndexes = [-1];
+
+ /// "请选择"tab宽度,添加新的tab时用到
+ double _animTabWidth = 0;
+
+ /// tab添加事件记录,用于隐藏"请选择"tab初始化状态
+ bool _isAddTabEvent = false;
+
+ /// tab移动未开始,渲染'请选择'tab时隐藏文本,这时的tab在终点位置
+ bool _isAnimateTextHide = false;
+
+ /// 防止_moveSlider重复调用
+ bool _isClickAndMoveTab = false;
+
+ /// 当前选择的页面,移动滑块前赋值
+ int _currentSelectPage = 0;
+
+ _addTab(int page, int atIndex, String currentPageItem) {
+ _loadNextPageData(page, atIndex, currentPageItem);
+ }
+
+ _loadNextPageData(int page, int atIndex, String currentPageItem,
+ {bool isUpdatePage = false}) {
+ widget.nextPageData((data) {
+ final nextPageDataIsEmpty = data.isEmpty;
+ if (!nextPageDataIsEmpty) {
+ /// 下一页有数据,更新本页数据或添加新的页面
+ setState(() {
+ if (isUpdatePage) {
+ /// 更新下一页
+ _pagesData[page] = data;
+ _selectedTabs[page] = _newTabName;
+ _selectedIndexes[page] = -1;
+
+ /// 清空下下页以后的所有页面和tab数据
+ _pagesData.removeRange(page + 1, _pagesData.length);
+ _selectedIndexes.removeRange(page + 1, _selectedIndexes.length);
+ _selectedTabs.removeRange(page + 1, _selectedTabs.length);
+ } else {
+ /// 添加新的页面
+ _isAnimateTextHide = true;
+ _isAddTabEvent = true;
+ _pagesData.add(data);
+ _selectedTabs.add(_newTabName);
+ _selectedIndexes.add(-1);
+ }
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ _moveSlider(page, isAdd: true);
+ });
+ });
+ } else {
+ /// 如果下一页数据为空,那么更新本页数据
+ final currentPage = page - 1;
+ setState(() {
+ _selectedTabs[currentPage] = currentPageItem;
+ _selectedIndexes[currentPage] = atIndex;
+
+ /// 下一页数据为空,清空下一页以后的所有页面和tab数据
+ _pagesData.removeRange(page, _pagesData.length);
+ _selectedIndexes.removeRange(page, _selectedIndexes.length);
+ _selectedTabs.removeRange(page, _selectedTabs.length);
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ // 调整滑块位置
+ _moveSlider(currentPage);
+ });
+ });
+ }
+ }, page, atIndex);
+ }
+
+ _moveSlider(int page, {bool movePage = true, bool isAdd = false}) {
+ if (movePage && _currentSelectPage != page) {
+ /// 上一次选择的页面和本次选择的页面不同时,移动tab标签,
+ /// 移动时先把_isClickAndMoveTab设为true,防止滑动PageView
+ /// 时_moveSlider重复调用。
+ _isClickAndMoveTab = true;
+ }
+ _isAddTabEvent = isAdd;
+ _currentSelectPage = page;
+
+ if (_controller.isAnimating) {
+ _controller.stop();
+ }
+ RenderBox slider =
+ _sliderKey.currentContext?.findRenderObject() as RenderBox;
+ Offset sliderPosition = slider.localToGlobal(Offset.zero);
+ RenderBox currentTabBox =
+ _tabKeys[page].currentContext?.findRenderObject() as RenderBox;
+ Offset currentTabPosition = currentTabBox.localToGlobal(Offset.zero);
+
+ _animTabWidth = currentTabBox.size.width;
+
+ final begin = sliderPosition.dx - _sliderFixMargin.value;
+ final end = currentTabPosition.dx +
+ (currentTabBox.size.width - _sliderWidth) / 2 -
+ _sliderFixMargin.value;
+ _sliderAnimation =
+ Tween(begin: begin, end: end).animate(_curvedAnimation);
+ _controller.value = 0;
+ _controller.forward();
+ if (movePage) {
+ _pageController.animateToPage(page,
+ curve: Curves.linear, duration: Duration(milliseconds: 500));
+ }
+ }
+
+ /// 注意:tab渲染完成才开始动画,即调用moveSlider,这个方法会在动画执行期间多次调用
+ Widget _animateTab({required Widget tab}) {
+ return Transform.translate(
+ offset: Offset(
+ Tween(begin: _isAddTabEvent ? -_animTabWidth : 0, end: 0)
+ .evaluate(_curvedAnimation),
+ 0),
+ child: Opacity(
+
+ /// 动画未开始前隐藏文本
+ opacity: _isAnimateTextHide ? 0 : 1,
+ child: tab),
+ );
+ }
+
+ List _tabWidgets() {
+ List widgets = [];
+ _tabKeys.clear();
+ for (int i = 0; i < _pagesData.length; i++) {
+ GlobalKey key = GlobalKey();
+ _tabKeys.add(key);
+ final tab = GestureDetector(
+ child: Container(
+ key: key,
+ height: widget.tabHeight,
+ color: widget.tabColor,
+ alignment: Alignment.center,
+ padding: EdgeInsets.symmetric(horizontal: 15),
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ maxWidth:
+ MediaQuery.of(context).size.width / _pagesData.length - 10),
+ child: Text(
+ _selectedTabs[i],
+ style: _currentSelectPage == i
+ ? widget.tabTitleStyle.copyWith(color: widget.activeColor)
+ : widget.tabTitleStyle,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ ),
+ onTap: () {
+ _moveSlider(i);
+ },
+ );
+ if (i == _pagesData.length - 1 && _selectedTabs[i] == _newTabName) {
+ widgets.add(_animateTab(tab: tab));
+ _isAnimateTextHide = false;
+ } else {
+ widgets.add(tab);
+ }
+ }
+ return widgets;
+ }
+
+ /// 选择项
+ Widget _pageItemWidget(int index, int page, String item) {
+ return GestureDetector(
+ child: Container(
+ alignment: Alignment.centerLeft,
+ padding: EdgeInsets.symmetric(horizontal: 15),
+ height: widget.itemHeight,
+ color: widget.itemColor,
+ child: Row(
+ children: [
+ item == _selectedTabs[page]
+ ? Padding(
+ padding: const EdgeInsets.all(5.0),
+ child: widget.selectedIcon == null
+ ? Icon(Icons.chevron_right,
+ size: 15, color: widget.activeColor)
+ : widget.selectedIcon,
+ )
+ : SizedBox(),
+ Text("$item",
+ style: item == _selectedTabs[page]
+ ? widget.itemTitleStyle.copyWith(color: widget.activeColor)
+ : widget.itemTitleStyle),
+ ],
+ ),
+ ),
+ onTap: () {
+ if (page == widget.maxPageNum - 1) {
+ /// 当前页是最后一页
+ setState(() {
+ _selectedTabs[page] = item;
+ _selectedIndexes[page] = index;
+
+ /// 调整滑块位置
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ _moveSlider(page);
+ });
+ });
+ } else if (_tabKeys.length >= widget.maxPageNum ||
+ page < _tabKeys.length - 1) {
+ if (index == _selectedIndexes[page]) {
+ /// 选择相同的item
+ _moveSlider(page + 1);
+ } else {
+ /// 选择不同的item,更新tab renderBox
+ setState(() {
+ _selectedTabs[page] = item;
+ _selectedIndexes[page] = index;
+// _selectedIndexes.removeRange(page + 1, _selectedIndexes.length);
+ });
+ _loadNextPageData(page + 1, index, item, isUpdatePage: true);
+ }
+ } else {
+ /// 添加新tab页面
+ /// page == _tabKeys.length - 1 && _tabKeys.length == widget.maxPageNum
+ _selectedTabs[page] = item;
+ _selectedIndexes[page] = index;
+ _addTab(page + 1, index, item);
+ }
+ },
+ );
+ }
+
+ Widget _pageWidget(int page) {
+ return ListView.builder(
+ padding: EdgeInsets.zero,
+ itemCount: _pagesData[page].length,
+ itemBuilder: (context, index) =>
+ _pageItemWidget(index, page, _pagesData[page][index]),
+// separatorBuilder: (context, index) => Divider(height: 0.3, thickness: 0.3, color: Color(0xffdddddd), indent: 15, endIndent: 15,),
+ );
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ _pagesData.add(widget.initialPageData);
+
+ _controller = AnimationController(
+ duration: const Duration(milliseconds: 500), vsync: this);
+
+ _curvedAnimation = CurvedAnimation(parent: _controller, curve: Curves.ease)
+ ..addStatusListener((state) {});
+
+ _sliderAnimation =
+ Tween(begin: 0, end: 10).animate(_curvedAnimation);
+
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ RenderBox tabBox =
+ _tabKeys.first.currentContext?.findRenderObject() as RenderBox;
+ _sliderFixMargin.value = (tabBox.size.width - _sliderWidth) / 2;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ AnimatedBuilder(
+ animation: _sliderAnimation!,
+ builder: (context, child) => Stack(
+ clipBehavior: Clip.hardEdge,
+ alignment: Alignment.bottomLeft,
+ children: [
+ Container(
+ width: MediaQuery.of(context).size.width,
+ child: Row(
+ children: _tabWidgets(),
+ ),
+ ),
+ ValueListenableBuilder(
+ valueListenable: _sliderFixMargin,
+ builder: (_, margin, __) => Positioned(
+ left: margin + _sliderAnimation!.value,
+ child: Container(
+ key: _sliderKey,
+ width: _sliderWidth,
+ height: 2,
+ decoration: BoxDecoration(
+ color: widget.activeColor,
+ borderRadius: BorderRadius.circular(2)),
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ Expanded(
+ child: PageView.builder(
+ itemCount: _pagesData.length,
+ controller: _pageController,
+ itemBuilder: (context, index) => _pageWidget(index),
+ onPageChanged: (position) {
+ if (!_isClickAndMoveTab) {
+ _moveSlider(position, movePage: false);
+ }
+ if (_currentSelectPage == position) {
+ _isClickAndMoveTab = false;
+ }
+ },
+ ),
+ )
+ ],
+ );
+ }
+}
+
+class CascadeController extends GetxController {
+ late List- items = [];
+ @override
+ void onInit() {
+ items = [];
+ for (int i = 0; i < 5; i++) {
+ Item item0 = Item();
+ item0.name = "name_$i";
+ item0.code = "code_$i";
+ List
- children1 = [];
+ for (int j = 0; j < 3; j++) {
+ Item item1 = Item();
+ item1.name = "name_${i}_$j";
+ item1.code = "code_${i}_$j";
+ List
- children2 = [];
+ for (int k = 0; k < 7; k++) {
+ Item item2 = Item();
+ item2.name = "name_${i}_${j}_$k";
+ item2.code = "code_${i}_${j}_$k";
+ // 第3页没有子数据列表
+ item2.children = [];
+ children2.add(item2);
+ }
+ // 第2页的子数据列表
+ item1.children = children2;
+ children1.add(item1);
+ }
+ // 第1页的子数据列表
+ item0.children = children1;
+ items.add(item0);
+ super.onInit();
+ }
+ }
+
+ late final _CascadePickerState _state;
+
+ _setState(_CascadePickerState state) {
+ _state = state;
+ }
+
+ List get selectedTitles => _state._selectedTabs;
+
+ List get selectedIndexes => _state._selectedIndexes;
+
+ bool isCompleted() =>
+ !_state._selectedTabs.contains(_CascadePickerState._newTabName);
+}
diff --git a/lib/widgets/core/sk_dialog_header.dart b/lib/widgets/core/sk_dialog_header.dart
new file mode 100644
index 0000000..000786b
--- /dev/null
+++ b/lib/widgets/core/sk_dialog_header.dart
@@ -0,0 +1,58 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/util/screen_adaper_util.dart';
+
+class SkDialogHeader extends StatelessWidget {
+ final String title;
+ final Widget? leading;
+ final Widget? trailing;
+ const SkDialogHeader({
+ super.key,
+ required this.title,
+ this.leading,
+ this.trailing,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ padding: const EdgeInsets.symmetric(),
+ decoration: const BoxDecoration(
+ gradient: LinearGradient(
+ colors: [AppTheme.primaryColorLight, AppTheme.primaryColor])),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ trailing ??
+ SizedBox(
+ width: ScreenAdaper.width(100),
+ ),
+ Expanded(
+ child: Text(
+ title,
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: ScreenAdaper.height(30),
+ color: AppTheme.nearlyWhite,
+ fontWeight: FontWeight.w600),
+ ),
+ ),
+ trailing ??
+ Container(
+ width: ScreenAdaper.width(100),
+ child: IconButton(
+ onPressed: () {
+ Get.back();
+ },
+ icon: Icon(
+ Icons.close,
+ size: ScreenAdaper.sp(45),
+ color: AppTheme.nearlyWhite,
+ )),
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/core/sk_ink.dart b/lib/widgets/core/sk_ink.dart
index b6e01a1..e9b5663 100644
--- a/lib/widgets/core/sk_ink.dart
+++ b/lib/widgets/core/sk_ink.dart
@@ -1,24 +1,48 @@
import 'package:flutter/material.dart';
import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/constants/bg_color.dart';
+import 'package:sk_base_mobile/util/screen_adaper_util.dart';
class SkInk extends StatelessWidget {
final Widget? child;
final void Function()? onTap;
final BorderRadius? borderRadius;
final BoxBorder? border;
+
+ final Color? color;
+ final EdgeInsets? padding;
+ final EdgeInsets? margin;
+ final Gradient? gradient;
const SkInk(
- {super.key, this.child, this.onTap, this.borderRadius, this.border});
+ {super.key,
+ this.child,
+ this.onTap,
+ this.color,
+ this.borderRadius,
+ this.border,
+ this.gradient,
+ this.margin,
+ this.padding});
@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)),
+ return Container(
+ margin: margin,
+ child: ClipRRect(
+ borderRadius: borderRadius ?? BorderRadius.zero,
+ child: Material(
+ child: Ink(
+ padding: padding,
+ decoration: BoxDecoration(
+ border: border,
+ color: color,
+ gradient: gradient,
+ borderRadius: borderRadius,
+ ),
+ child: InkWell(
+ borderRadius: borderRadius, onTap: onTap, child: child)),
+ ),
+ ),
);
}
}
diff --git a/lib/widgets/core/sk_tag.dart b/lib/widgets/core/sk_tag.dart
index 61dc925..52359cc 100644
--- a/lib/widgets/core/sk_tag.dart
+++ b/lib/widgets/core/sk_tag.dart
@@ -1,4 +1,3 @@
-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';
diff --git a/lib/widgets/core/sk_text_input.dart b/lib/widgets/core/sk_text_input.dart
index adaada7..6756354 100644
--- a/lib/widgets/core/sk_text_input.dart
+++ b/lib/widgets/core/sk_text_input.dart
@@ -20,6 +20,7 @@ class SkTextInput extends StatefulWidget {
final Widget? suffixIcon;
final InputBorder? border;
final FloatingLabelBehavior? floatingLabelBehavior;
+ final TextInputType? keyboardType;
const SkTextInput(
{super.key,
required this.textController,
@@ -28,6 +29,7 @@ class SkTextInput extends StatefulWidget {
this.onFieldSubmitted,
this.isRequired = false,
this.onTapOutside,
+ this.keyboardType,
this.labelText,
this.prefix,
this.suffixIcon,
@@ -79,6 +81,7 @@ class _SkTextInputState extends State {
widget.onTap!(focusNode);
}
},
+ keyboardType: widget.keyboardType,
onFieldSubmitted: widget.onFieldSubmitted,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: widget.validator,
diff --git a/lib/widgets/fade_in_cache_image.dart b/lib/widgets/fade_in_cache_image.dart
index 52be026..0326cf1 100644
--- a/lib/widgets/fade_in_cache_image.dart
+++ b/lib/widgets/fade_in_cache_image.dart
@@ -54,11 +54,17 @@ class _FadeInCacheImageState extends State {
Widget buildImg(String? url) {
return CachedNetworkImage(
- alignment: Alignment.center,
imageUrl: url ?? '',
width: widget.width,
height: widget.height,
- fit: widget.fit,
+ imageBuilder: (context, imageProvider) => Container(
+ decoration: BoxDecoration(
+ image: DecorationImage(
+ image: imageProvider,
+ fit: widget.fit ?? BoxFit.cover,
+ ),
+ ),
+ ),
placeholder: (context, url) => Container(
decoration: const BoxDecoration(color: AppTheme.grey),
child: const CupertinoActivityIndicator(),
@@ -70,12 +76,10 @@ class _FadeInCacheImageState extends State {
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.height((widget.width ?? 200) * 3 / 4),
+ size: ScreenAdaper.height((widget.width ?? 200)),
color: AppTheme.grey),
);
}
diff --git a/lib/widgets/gradient_button.dart b/lib/widgets/gradient_button.dart
index d90220c..363791a 100644
--- a/lib/widgets/gradient_button.dart
+++ b/lib/widgets/gradient_button.dart
@@ -3,63 +3,100 @@ import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:sk_base_mobile/app_theme.dart';
import 'package:sk_base_mobile/constants/constants.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
+import 'package:sk_base_mobile/widgets/core/sk_ink.dart';
class GradientButton extends StatelessWidget {
final VoidCallback? onPressed;
final bool isLoading;
final String buttonText;
final Icon? icon;
+ final BorderRadiusGeometry? borderRadius;
const GradientButton(
{super.key,
this.buttonText = TextEnum.createInventoryInOutBtnText,
this.onPressed,
this.icon,
+ this.borderRadius,
this.isLoading = false});
@override
Widget build(BuildContext context) {
- return SizedBox(
- width: double.infinity,
- child: ElevatedButton(
- style: ButtonStyle(
- backgroundColor:
- MaterialStateProperty.all(AppTheme.primaryColor),
- padding: MaterialStateProperty.all(
- EdgeInsets.symmetric(vertical: ScreenAdaper.height(10)),
- ),
- minimumSize: MaterialStateProperty.all(
- Size(double.infinity, ScreenAdaper.height(80))),
- shape: MaterialStateProperty.all(
- RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(10),
- ),
- ),
- ),
- onPressed: onPressed ?? () => {},
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- if (icon != null) ...[
- icon!,
- SizedBox(
- width: ScreenAdaper.width(10),
- )
- ],
- isLoading
- ? LoadingAnimationWidget.fourRotatingDots(
- color: AppTheme.nearlyWhite,
- size: ScreenAdaper.height(40),
- )
- : Text(
- buttonText,
- style: TextStyle(
+ return SkInk(
+ onTap: onPressed ?? () {},
+ gradient: const LinearGradient(
+ colors: [AppTheme.primaryColorLight, AppTheme.primaryColor]),
+ child: SizedBox(
+ height: ScreenAdaper.height(80),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ if (icon != null) ...[
+ icon!,
+ SizedBox(
+ width: ScreenAdaper.width(10),
+ )
+ ],
+ isLoading
+ ? LoadingAnimationWidget.fourRotatingDots(
color: AppTheme.nearlyWhite,
- fontWeight: FontWeight.bold,
- fontSize: ScreenAdaper.height(25),
- ),
- )
- ]),
- ),
+ size: ScreenAdaper.height(40),
+ )
+ : Text(
+ buttonText,
+ style: TextStyle(
+ color: AppTheme.nearlyWhite,
+ fontWeight: FontWeight.bold,
+ fontSize: ScreenAdaper.height(25),
+ ),
+ )
+ ])),
);
+
+ // ElevatedButton(
+ // style: ButtonStyle(
+ // backgroundColor:
+ // MaterialStateProperty.all(AppTheme.primaryColor),
+ // minimumSize: MaterialStateProperty.all(
+ // Size(double.infinity, ScreenAdaper.height(80))),
+ // shape: MaterialStateProperty.all(
+ // RoundedRectangleBorder(
+ // borderRadius: borderRadius ?? BorderRadius.circular(10),
+ // ),
+ // ),
+ // ),
+ // onPressed: onPressed ?? () => {},
+ // child: Text(
+ // buttonText,
+ // style: TextStyle(
+ // color: AppTheme.nearlyWhite,
+ // fontWeight: FontWeight.bold,
+ // fontSize: ScreenAdaper.height(25),
+ // ),
+ // ),
+ // child: Row(
+ // crossAxisAlignment: CrossAxisAlignment.center,
+ // mainAxisAlignment: MainAxisAlignment.center,
+ // children: [
+ // if (icon != null) ...[
+ // icon!,
+ // SizedBox(
+ // width: ScreenAdaper.width(10),
+ // )
+ // ],
+ // isLoading
+ // ? LoadingAnimationWidget.fourRotatingDots(
+ // color: AppTheme.nearlyWhite,
+ // size: ScreenAdaper.height(40),
+ // )
+ // : Text(
+ // buttonText,
+ // style: TextStyle(
+ // color: AppTheme.nearlyWhite,
+ // fontWeight: FontWeight.bold,
+ // fontSize: ScreenAdaper.height(25),
+ // ),
+ // )
+ // ]),
+ // );
}
}
diff --git a/lib/widgets/loading_indicator.dart b/lib/widgets/loading_indicator.dart
index c854792..e741043 100644
--- a/lib/widgets/loading_indicator.dart
+++ b/lib/widgets/loading_indicator.dart
@@ -14,10 +14,13 @@ class LoadingIndicator extends StatelessWidget {
return common
? LoadingAnimationWidget.fourRotatingDots(
color: AppTheme.primaryColorLight, size: ScreenAdaper.height(45))
- : CupertinoActivityIndicator(
- animating: animating,
- color: AppTheme.primaryColor,
- radius: ScreenAdaper.sp(25),
+ : Container(
+ padding: EdgeInsets.all(ScreenAdaper.height(10)),
+ child: CupertinoActivityIndicator(
+ animating: animating,
+ color: AppTheme.primaryColor,
+ radius: ScreenAdaper.sp(25),
+ ),
);
}
}
diff --git a/lib/widgets/my_avatar.dart b/lib/widgets/my_avatar.dart
index 97a45b1..5cb18ed 100644
--- a/lib/widgets/my_avatar.dart
+++ b/lib/widgets/my_avatar.dart
@@ -1,9 +1,11 @@
import 'dart:io';
+import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:sk_base_mobile/store/auth.store.dart';
import 'package:sk_base_mobile/app_theme.dart';
+import 'package:sk_base_mobile/widgets/fade_in_cache_image.dart';
import '../util/util.dart';
@@ -15,17 +17,7 @@ class MyAvatarWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Stack(
children: [
- Obx(() => Container(
- decoration: BoxDecoration(
- image:
- DecorationImage(fit: BoxFit.cover, image: _buildImage()),
- border: Border.all(
- color: AppTheme.white, width: ScreenAdaper.sp(2)),
- borderRadius: BorderRadius.circular(50)),
- height: ScreenAdaper.width(150),
- width: ScreenAdaper.width(150),
- child: const SizedBox(),
- )),
+ Obx(() => _buildAvatar()),
Positioned(
bottom: 0,
right: 0,
@@ -41,11 +33,16 @@ class MyAvatarWidget extends StatelessWidget {
);
}
- dynamic _buildImage() {
+ dynamic _buildAvatar() {
return _controller.uploadImgFilePath.value.isNotEmpty
? FileImage(File(_controller.uploadImgFilePath.value))
- : NetworkImage(
- AuthStore.to.userInfo.value.avatar ?? '',
+ : ClipRRect(
+ borderRadius: BorderRadius.circular(ScreenAdaper.sp(120)),
+ child: FadeInCacheImage(
+ height: ScreenAdaper.height(120),
+ width: ScreenAdaper.height(120),
+ url: MediaUtil.getMediaUrl(AuthStore.to.userInfo.value.avatar),
+ ),
);
}
// Widget getShowImg() {
diff --git a/lib/widgets/upgrade_confirm.dart b/lib/widgets/upgrade_confirm.dart
index e8597e1..95d453b 100644
--- a/lib/widgets/upgrade_confirm.dart
+++ b/lib/widgets/upgrade_confirm.dart
@@ -67,7 +67,7 @@ class UpgradeConfirm extends StatelessWidget {
SizedBox(
height: ScreenAdaper.height(30),
),
- Container(
+ SizedBox(
width: ScreenAdaper.height(400),
child: GradientButton(
buttonText: '去更新',
diff --git a/pubspec.lock b/pubspec.lock
index b098a7f..a180c01 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1005,6 +1005,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.2"
+ url_launcher:
+ dependency: "direct main"
+ description:
+ name: url_launcher
+ sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.2.5"
+ url_launcher_android:
+ dependency: transitive
+ description:
+ name: url_launcher_android
+ sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.3.0"
+ url_launcher_ios:
+ dependency: transitive
+ description:
+ name: url_launcher_ios
+ sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.2.5"
+ url_launcher_linux:
+ dependency: transitive
+ description:
+ name: url_launcher_linux
+ sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.1"
+ url_launcher_macos:
+ dependency: transitive
+ description:
+ name: url_launcher_macos
+ sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.0"
+ url_launcher_platform_interface:
+ dependency: transitive
+ description:
+ name: url_launcher_platform_interface
+ sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.2"
+ url_launcher_web:
+ dependency: transitive
+ description:
+ name: url_launcher_web
+ sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.3"
+ url_launcher_windows:
+ dependency: transitive
+ description:
+ name: url_launcher_windows
+ sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.1"
uuid:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 84493cd..e6be3f6 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -67,6 +67,7 @@ dependencies:
pinyin: ^3.2.0
math_expressions: ^2.4.0
install_plugin: ^2.1.0
+ url_launcher: ^6.2.5
dev_dependencies:
flutter_test:
sdk: flutter