|
@@ -0,0 +1,498 @@
|
|
|
+import 'package:cs_resources/constants/color_constants.dart';
|
|
|
+import 'package:cs_resources/generated/assets.dart';
|
|
|
+import 'package:flutter/cupertino.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:flutter/widgets.dart';
|
|
|
+import 'package:get/get.dart';
|
|
|
+import 'package:plugin_basic/base/base_stateful_page.dart';
|
|
|
+import 'package:plugin_basic/base/base_state.dart';
|
|
|
+import 'package:plugin_basic/base/mixin_state_lifecycle.dart';
|
|
|
+import 'package:plugin_basic/utils/ext_get_nav.dart';
|
|
|
+import 'package:plugin_platform/engine/toast/toast_engine.dart';
|
|
|
+import 'package:router/path/router_path.dart';
|
|
|
+import 'package:shared/utils/log_utils.dart';
|
|
|
+import 'package:shared/utils/screen_util.dart';
|
|
|
+import 'package:shared/utils/util.dart';
|
|
|
+import 'package:widgets/ext/ex_widget.dart';
|
|
|
+import 'package:widgets/load_state_layout.dart';
|
|
|
+import 'package:widgets/my_button.dart';
|
|
|
+import 'package:widgets/my_load_image.dart';
|
|
|
+import 'package:widgets/my_text_field.dart';
|
|
|
+import 'package:widgets/my_text_view.dart';
|
|
|
+import 'package:widgets/widget_export.dart';
|
|
|
+import 'sign_in_sign_out_controller.dart';
|
|
|
+import 'sign_in_sign_out_state.dart';
|
|
|
+
|
|
|
+/*
|
|
|
+ 工作的签到页面
|
|
|
+ */
|
|
|
+class SignInSignOutPage extends BaseStatefulPage<SignInSignOutController> {
|
|
|
+ SignInSignOutPage({super.key});
|
|
|
+
|
|
|
+ //启动当前页面
|
|
|
+ static void startInstance() {
|
|
|
+ return Get.start(RouterPath.JOB_SIGN_IN_SIGN_OUT, launchModel: LaunchModel.singleTask);
|
|
|
+ }
|
|
|
+
|
|
|
+ static void startWithPop() {
|
|
|
+ Get.startWithPop(RouterPath.JOB_SIGN_IN_SIGN_OUT);
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ State<SignInSignOutPage> createState() => _SignInSignOutPageState();
|
|
|
+
|
|
|
+ @override
|
|
|
+ SignInSignOutController createRawController() {
|
|
|
+ return SignInSignOutController();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _SignInSignOutPageState extends BaseState<SignInSignOutPage, SignInSignOutController> with StateLifecycle {
|
|
|
+ late SignInSignOutState state;
|
|
|
+
|
|
|
+ @override
|
|
|
+ void initState() {
|
|
|
+ super.initState();
|
|
|
+ state = controller.state;
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void dispose() {
|
|
|
+ Get.delete<SignInSignOutController>();
|
|
|
+ super.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void onResume() {
|
|
|
+ super.onResume();
|
|
|
+ Log.d("SignInSignOutPage Lifecycle - onResume");
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void onPause() {
|
|
|
+ super.onPause();
|
|
|
+ Log.d("SignInSignOutPage Lifecycle - onPause");
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void onStop() {
|
|
|
+ super.onStop();
|
|
|
+ Log.d("SignInSignOutPage Lifecycle - onStop");
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void onStart() {
|
|
|
+ super.onStart();
|
|
|
+ Log.d("SignInSignOutPage Lifecycle - onStart");
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ Log.d("SignInSignOutPage Lifecycle - build走了一遍");
|
|
|
+
|
|
|
+ return autoCtlGetBuilder(
|
|
|
+ builder: (controller) {
|
|
|
+ return Scaffold(
|
|
|
+ body: Container(
|
|
|
+ width: double.infinity,
|
|
|
+ height: double.infinity,
|
|
|
+ decoration: const BoxDecoration(
|
|
|
+ gradient: LinearGradient(
|
|
|
+ colors: [
|
|
|
+ Color(0xFF091D44),
|
|
|
+ Color(0xFF245A8A),
|
|
|
+ Color(0xFF7F7CEC),
|
|
|
+ ],
|
|
|
+ begin: Alignment.topCenter,
|
|
|
+ end: Alignment.bottomCenter,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ //顶部的搜索标题布局
|
|
|
+ _buildSearchTitleBar(),
|
|
|
+
|
|
|
+ Container(color: Color(0XFF415B7C), height: 0.5, margin: EdgeInsets.only(top: 12)),
|
|
|
+
|
|
|
+ _buildFilterDateWidget(),
|
|
|
+
|
|
|
+ EasyRefresh(
|
|
|
+ controller: controller.refreshController,
|
|
|
+ onRefresh: controller.onRefresh,
|
|
|
+ child: LoadStateLayout(
|
|
|
+ state: controller.loadingState,
|
|
|
+ errorMessage: controller.errorMessage,
|
|
|
+ errorRetry: () {
|
|
|
+ controller.retryRequest();
|
|
|
+ },
|
|
|
+ successSliverWidget: [
|
|
|
+ SliverList(
|
|
|
+ delegate: SliverChildBuilderDelegate(
|
|
|
+ (context, index) {
|
|
|
+ return _buildAttendanceItem(controller, index, () {});
|
|
|
+ },
|
|
|
+ childCount: controller.datas.length,
|
|
|
+ ))
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ).expanded(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ },
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 顶部的搜索栏布局
|
|
|
+ Widget _buildSearchTitleBar() {
|
|
|
+ return Row(
|
|
|
+ mainAxisSize: MainAxisSize.max,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Container(
|
|
|
+ height: 35,
|
|
|
+ padding: EdgeInsets.only(left: 15, right: 11),
|
|
|
+ margin: EdgeInsets.only(left: 15, right: 10),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
|
|
|
+ borderRadius: BorderRadius.circular(17.25), // 设置圆角
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ mainAxisSize: MainAxisSize.max,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ //输入框
|
|
|
+ IgnoreKeyboardDismiss(
|
|
|
+ child: TextField(
|
|
|
+ cursorColor: ColorConstants.white,
|
|
|
+ cursorWidth: 1.5,
|
|
|
+ autofocus: false,
|
|
|
+ maxLines: 1,
|
|
|
+ minLines: 1,
|
|
|
+ // 是否自动获取焦点
|
|
|
+ focusNode: state.formData['keyword']!['focusNode'],
|
|
|
+ // 焦点控制
|
|
|
+ controller: state.formData['keyword']!['controller'],
|
|
|
+ // 与输入框交互控制器
|
|
|
+ //装饰
|
|
|
+ decoration: InputDecoration(
|
|
|
+ isDense: true,
|
|
|
+ //清除垂直方向的填充
|
|
|
+ isCollapsed: true,
|
|
|
+ //让文字垂直居中
|
|
|
+ border: InputBorder.none,
|
|
|
+ hintText: state.formData['keyword']!['hintText'],
|
|
|
+ hintStyle: TextStyle(
|
|
|
+ color: hexToColor("#AECAE5"),
|
|
|
+ fontSize: 15.0,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ style: TextStyle(
|
|
|
+ color: ColorConstants.white,
|
|
|
+ fontSize: 15.0,
|
|
|
+ fontWeight: FontWeight.w400,
|
|
|
+ ),
|
|
|
+ // 键盘动作右下角图标
|
|
|
+ textInputAction: TextInputAction.search,
|
|
|
+ textAlignVertical: TextAlignVertical.center,
|
|
|
+ onSubmitted: (value) {
|
|
|
+ controller.doSearch();
|
|
|
+ }, //输入框完成触发
|
|
|
+ ),
|
|
|
+ ).expanded(),
|
|
|
+
|
|
|
+ //搜索图标
|
|
|
+ MyAssetImage(Assets.cptJobSearchIcon, width: 15, height: 15).marginOnly(left: 10).onTap(() {
|
|
|
+ controller.doSearch();
|
|
|
+ }),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ).expanded(),
|
|
|
+ MyButton(
|
|
|
+ onPressed: () {
|
|
|
+ controller.resetFiltering();
|
|
|
+ },
|
|
|
+ text: "Reset".tr,
|
|
|
+ textColor: ColorConstants.white,
|
|
|
+ backgroundColor: hexToColor("#2BA9F9", opacity: 0.5),
|
|
|
+ radius: 17.25,
|
|
|
+ minWidth: 60,
|
|
|
+ minHeight: 35,
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ ).marginOnly(top: ScreenUtil.getStatusBarH(context) + 5, right: 12);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 时间筛选布局
|
|
|
+ Widget _buildFilterDateWidget() {
|
|
|
+ return Container(
|
|
|
+ width: double.infinity,
|
|
|
+ height: 36,
|
|
|
+ margin: EdgeInsets.only(left: 15, right: 15, top: 15, bottom: 10),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
|
|
|
+ borderRadius: BorderRadius.circular(17.25), // 设置圆角
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ mainAxisSize: MainAxisSize.max,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ MyTextView(
|
|
|
+ "2024-6-27",
|
|
|
+ fontSize: 16,
|
|
|
+ textAlign: TextAlign.center,
|
|
|
+ isFontRegular: true,
|
|
|
+ textColor: ColorConstants.white,
|
|
|
+ onClick: (){
|
|
|
+ controller.pickerStartDate();
|
|
|
+ },
|
|
|
+ ).expanded(),
|
|
|
+
|
|
|
+ Container(color: Color(0XFF52739C),width: 0.5,height: 21.5,),
|
|
|
+
|
|
|
+ MyTextView(
|
|
|
+ "2024-6-27",
|
|
|
+ fontSize: 16,
|
|
|
+ textAlign: TextAlign.center,
|
|
|
+ isFontRegular: true,
|
|
|
+ textColor: ColorConstants.white,
|
|
|
+ onClick: (){
|
|
|
+ controller.pickerEndDate();
|
|
|
+ },
|
|
|
+ ).expanded(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Item列表
|
|
|
+ Widget _buildAttendanceItem(SignInSignOutController controller, int index, void Function() callback) {
|
|
|
+ final item = controller.datas[index];
|
|
|
+
|
|
|
+ return Container(
|
|
|
+ padding: EdgeInsets.only(left: 23, right: 12),
|
|
|
+ margin: EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Color(0xFF4DCFF6).withOpacity(0.2), // 设置背景颜色和不透明度
|
|
|
+ borderRadius: BorderRadius.circular(5), // 设置圆角
|
|
|
+ ),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ Row(
|
|
|
+ mainAxisSize: MainAxisSize.max,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ MyTextView(
|
|
|
+ "Name:".tr,
|
|
|
+ isFontRegular: true,
|
|
|
+ textColor: Color(0XFFAECAE5),
|
|
|
+ fontSize: 14,
|
|
|
+ ),
|
|
|
+
|
|
|
+ //姓名
|
|
|
+ MyTextView(
|
|
|
+ item.staffName ?? "-",
|
|
|
+ marginLeft: 5,
|
|
|
+ isFontRegular: true,
|
|
|
+ textColor: Color(0XFFAECAE5),
|
|
|
+ fontSize: 14,
|
|
|
+ ).expanded(),
|
|
|
+
|
|
|
+ MyAssetImage(item.isExpended ? Assets.cptJobArrawUpIcon : Assets.cptJobArrawDownIcon, width: 15, height: 7),
|
|
|
+ ],
|
|
|
+ ).paddingOnly(top: 19, bottom: 19),
|
|
|
+
|
|
|
+ //显示隐藏的布局
|
|
|
+ Visibility(
|
|
|
+ visible: item.isExpended,
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ //工作日期
|
|
|
+ Row(
|
|
|
+ mainAxisSize: MainAxisSize.max,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ MyTextView(
|
|
|
+ "Job Date:".tr,
|
|
|
+ isFontRegular: true,
|
|
|
+ textColor: Color(0XFFAECAE5),
|
|
|
+ fontSize: 14,
|
|
|
+ ),
|
|
|
+
|
|
|
+ //姓名
|
|
|
+ MyTextView(
|
|
|
+ item.jobDate ?? "-",
|
|
|
+ marginLeft: 5,
|
|
|
+ isFontRegular: true,
|
|
|
+ textColor: Color(0XFFAECAE5),
|
|
|
+ fontSize: 14,
|
|
|
+ ).expanded(),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+
|
|
|
+ //开始时间
|
|
|
+ Row(
|
|
|
+ mainAxisSize: MainAxisSize.max,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ MyTextView(
|
|
|
+ "Start Time:",
|
|
|
+ isFontRegular: true,
|
|
|
+ textColor: Color(0XFFAECAE5),
|
|
|
+ fontSize: 14,
|
|
|
+ ),
|
|
|
+
|
|
|
+ //姓名
|
|
|
+ MyTextView(
|
|
|
+ item.startTime ?? "-",
|
|
|
+ marginLeft: 5,
|
|
|
+ isFontRegular: true,
|
|
|
+ textColor: Color(0XFFAECAE5),
|
|
|
+ fontSize: 14,
|
|
|
+ ).expanded(),
|
|
|
+ ],
|
|
|
+ ).marginOnly(top: 16),
|
|
|
+
|
|
|
+ //结束时间
|
|
|
+ Row(
|
|
|
+ mainAxisSize: MainAxisSize.max,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ MyTextView(
|
|
|
+ "End Time:".tr,
|
|
|
+ isFontRegular: true,
|
|
|
+ textColor: Color(0XFFAECAE5),
|
|
|
+ fontSize: 14,
|
|
|
+ ),
|
|
|
+
|
|
|
+ //姓名
|
|
|
+ MyTextView(
|
|
|
+ item.endTime ?? "-",
|
|
|
+ marginLeft: 5,
|
|
|
+ isFontRegular: true,
|
|
|
+ textColor: Color(0XFFAECAE5),
|
|
|
+ fontSize: 14,
|
|
|
+ ).expanded(),
|
|
|
+ ],
|
|
|
+ ).marginOnly(top: 16),
|
|
|
+
|
|
|
+ //签到的控件
|
|
|
+ Row(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ mainAxisAlignment: MainAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ //开始签到
|
|
|
+ Column(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.start,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Container(
|
|
|
+ width: 90,
|
|
|
+ height: 35,
|
|
|
+ margin: EdgeInsets.only(top: 18),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Utils.isEmpty(item.checkInImg) ? Color(0xFF56AAFF) : ColorConstants.white,
|
|
|
+ borderRadius: BorderRadius.circular(17.25), // 设置圆角
|
|
|
+ ),
|
|
|
+ child: Stack(
|
|
|
+ alignment: Alignment.center,
|
|
|
+ children: [
|
|
|
+ Visibility(
|
|
|
+ visible: Utils.isEmpty(item.checkInImg),
|
|
|
+ child: MyTextView(
|
|
|
+ "Check In".tr,
|
|
|
+ isFontMedium: true,
|
|
|
+ textColor: ColorConstants.white,
|
|
|
+ fontSize: 14,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Visibility(
|
|
|
+ visible: !Utils.isEmpty(item.checkInImg),
|
|
|
+ child: MyLoadImage(item.checkInImg, width: 71.5, height: 25, fit: BoxFit.cover),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ).onTap(() {
|
|
|
+ if (Utils.isEmpty(item.checkInImg)) {
|
|
|
+ controller.userSignIn(item);
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ ),
|
|
|
+ Visibility(
|
|
|
+ visible: !Utils.isEmpty(item.checkInTime),
|
|
|
+ child: MyTextView(
|
|
|
+ marginTop: 9,
|
|
|
+ item.checkInTime ?? "-",
|
|
|
+ textColor: ColorConstants.white,
|
|
|
+ fontSize: 14,
|
|
|
+ isFontRegular: true,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+
|
|
|
+ //结束签到
|
|
|
+ Column(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.start,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ Container(
|
|
|
+ width: 90,
|
|
|
+ height: 35,
|
|
|
+ margin: EdgeInsets.only(top: 18),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Utils.isEmpty(item.checkOutImg) ? Color(0xFF56AAFF) : ColorConstants.white,
|
|
|
+ borderRadius: BorderRadius.circular(17.25), // 设置圆角
|
|
|
+ ),
|
|
|
+ child: Stack(
|
|
|
+ alignment: Alignment.center,
|
|
|
+ children: [
|
|
|
+ Visibility(
|
|
|
+ visible: Utils.isEmpty(item.checkOutImg),
|
|
|
+ child: MyTextView(
|
|
|
+ "Check Out".tr,
|
|
|
+ isFontMedium: true,
|
|
|
+ textColor: ColorConstants.white,
|
|
|
+ fontSize: 14,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Visibility(
|
|
|
+ visible: !Utils.isEmpty(item.checkOutImg),
|
|
|
+ child: MyLoadImage(item.checkOutImg, width: 71.5, height: 25, fit: BoxFit.cover),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ).onTap(() {
|
|
|
+ if (Utils.isEmpty(item.checkOutImg)) {
|
|
|
+ controller.userSignOut(item);
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ ),
|
|
|
+ Visibility(
|
|
|
+ visible: !Utils.isEmpty(item.checkOutTime),
|
|
|
+ child: MyTextView(
|
|
|
+ marginTop: 9,
|
|
|
+ item.checkOutTime ?? "-",
|
|
|
+ textColor: ColorConstants.white,
|
|
|
+ fontSize: 14,
|
|
|
+ isFontRegular: true,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ).marginOnly(left: 10),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+
|
|
|
+ SizedBox(height: 18),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ).onTap(() {
|
|
|
+ //切换展开收起
|
|
|
+ item.isExpended = !item.isExpended;
|
|
|
+ controller.update();
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|