设施预约功能实现

pull/1/head
戴余标 3 years ago
parent bf9958caed
commit a693c81e91

@ -1,4 +1,4 @@
{
"flutterSdkVersion": "3.0.0",
"flutterSdkVersion": "2.8.1",
"flavors": {}
}

@ -40,11 +40,11 @@ class FacilityAppointmentModel {
Color get statusColor {
switch (status) {
case 1:
return kPrimaryColor;
return kBalckSubColor;
case 2:
return Color(0xFF2576E5);
return kDarkPrimaryColor;
case 3:
return Color(0xFFFB4702);
return kDangerColor;
case 4:
return Color(0xFF999999);
case 5:
@ -57,15 +57,15 @@ class FacilityAppointmentModel {
String get statusValue {
switch (status) {
case 1:
return '预约成功';
return '待签到';
case 2:
return '签到成功';
return '使用中';
case 3:
return '预约作废';
return '超时作废';
case 4:
return '取消';
return '取消预约';
case 5:
return '结束';
return '使用结束';
default:
return '未知状态';
}

@ -0,0 +1,20 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:equatable/equatable.dart';
part 'test.g.dart';
@JsonSerializable()
class Test extends Equatable {
final int id;
factory Test.fromJson(Map<String, dynamic> json) => _$TestFromJson(json);
@override
List<Object?> get props => [
id,
];
const Test({
required this.id,
});
}

@ -0,0 +1,11 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'test.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Test _$TestFromJson(Map<String, dynamic> json) => Test(
id: json['id'] as int,
);

@ -123,7 +123,8 @@ class UserProvider extends ChangeNotifier {
'' +
_defaultHouse!.unitName +
'单元' +
_defaultHouse!.estateName;
_defaultHouse!.estateName +
'';
}
}

@ -31,7 +31,7 @@ class Member {
final int id;
final String name;
final int identity;
final List<ImgModel> avatarImgList;
final List<ImgModel> imgList;
factory Member.fromJson(Map<String, dynamic> json) => _$MemberFromJson(json);
@ -39,6 +39,6 @@ class Member {
required this.id,
required this.name,
required this.identity,
required this.avatarImgList,
required this.imgList,
});
}

@ -23,7 +23,7 @@ Member _$MemberFromJson(Map<String, dynamic> json) => Member(
id: json['id'] as int,
name: json['name'] as String,
identity: json['identity'] as int,
avatarImgList: (json['avatarImgList'] as List<dynamic>)
imgList: (json['imgList'] as List<dynamic>)
.map((e) => ImgModel.fromJson(e as Map<String, dynamic>))
.toList(),
);

@ -1,3 +1,4 @@
import 'package:aku_new_community/ui/community/facility/pick_facility_page.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@ -36,11 +37,16 @@ class FacilityAppointmentCard extends StatelessWidget {
Text(
name,
style: TextStyle(
color: ktextSubColor,
color: ktextThirdColor,
),
),
Spacer(),
Text(subTitle),
Text(
subTitle,
style: TextStyle(
color: ktextSubColor,
),
),
],
);
}
@ -57,13 +63,17 @@ class FacilityAppointmentCard extends StatelessWidget {
bool inTime = diffTime >= 0 && diffTime <= 30;
if (inTime)
button = _FacilityButton(
bold: true,
onPressed: () async {
var result = await BeeQR.scan();
if (result != null) {
final cancel = BotToast.showLoading();
await NetUtil().get(
SAASAPI.facilities.signIn,
params: {'appointmentCode': result},
params: {
'facilitiesReserveId': model.id,
'appointmentCode': result
},
showMessage: true,
);
cancel();
@ -74,6 +84,9 @@ class FacilityAppointmentCard extends StatelessWidget {
);
else
button = _FacilityButton(
outline: true,
border: true,
bold: true,
onPressed: () async {
bool? result = await Get.dialog(
CupertinoAlertDialog(
@ -95,7 +108,7 @@ class FacilityAppointmentCard extends StatelessWidget {
final cancel = BotToast.showLoading();
await NetUtil().get(
SAASAPI.facilities.cancel,
params: {'facilitiesAppointmentId': model.id},
params: {'facilitiesReserveId': model.id},
showMessage: true,
);
cancel();
@ -107,11 +120,12 @@ class FacilityAppointmentCard extends StatelessWidget {
break;
case 2:
button = _FacilityButton(
bold: true,
onPressed: () async {
final cancel = BotToast.showLoading();
await NetUtil().get(
SAASAPI.facilities.useStop,
params: {'facilitiesAppointmentId': model.id},
params: {'facilitiesReserveId': model.id},
showMessage: true,
);
cancel();
@ -120,24 +134,62 @@ class FacilityAppointmentCard extends StatelessWidget {
text: '使用结束',
);
break;
default:
button = SizedBox();
button = _FacilityButton(
outline: true,
border: true,
bold: true,
textColor: ktextSubColor,
onPressed: () async {
await Get.to(() => PickFacilityPage());
},
text: '重新预约',
);
}
return Row(
children: [
if (showTip)
Text(
'请在预约时间前30分钟内到场扫码',
style: TextStyle(
color: ktextSubColor,
fontSize: 24.sp,
),
),
Spacer(),
button,
],
);
return model.status == 3
? Column(
children: [
Container(
color: Colors.black12.withOpacity(0.25),
padding: EdgeInsets.all(10.w),
child: Text(
'作废原因:${model.nullifyReason??''}',
style: TextStyle(
color: Colors.black.withOpacity(0.8),
fontSize: 24.sp,
),
),
),
Row(
children: [
if (showTip)
Text(
'请在预约时间前30分钟内到场扫码',
style: TextStyle(
color: ktextSubColor,
fontSize: 24.sp,
),
),
Spacer(),
button,
],
),
],
)
: Row(
children: [
if (showTip)
Text(
'请在预约时间前30分钟内到场扫码',
style: TextStyle(
color: ktextThirdColor,
fontSize: 24.sp,
),
),
Spacer(),
button,
],
);
}
@override
@ -202,6 +254,8 @@ class _FacilityButton extends StatelessWidget {
final VoidCallback onPressed;
final String text;
final bool outline;
final bool border;
final bool bold;
const _FacilityButton({
Key? key,
@ -210,13 +264,21 @@ class _FacilityButton extends StatelessWidget {
required this.text,
this.outline = false,
this.textColor = ktextPrimary,
this.border = false,
this.bold = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialButton(
color: outline ? null : color,
shape: StadiumBorder(),
shape: border
? StadiumBorder(
side: BorderSide(
width: 1,
color: Colors.grey,
))
: StadiumBorder(),
elevation: 0,
height: 60.w,
minWidth: 168.w,
@ -225,6 +287,7 @@ class _FacilityButton extends StatelessWidget {
child: Text(
text,
style: TextStyle(
fontWeight: bold?FontWeight.bold:FontWeight.normal,
color: textColor,
fontSize: 26.sp,
),

@ -14,6 +14,15 @@ import '../../../utils/network/base_model.dart';
import '../../../utils/network/net_util.dart';
import '../../../widget/buttons/bee_check_radio.dart';
class FacilityPreorderDate {
static Future<List<int>> choose(
FacilityTypeDetailModel typeModel,
) async {
return await Get.bottomSheet(
FacilityPreorderDatePicker(typeModel: typeModel));
}
}
class FacilityPreorderDatePicker extends StatefulWidget {
FacilityTypeDetailModel typeModel;
@ -35,14 +44,15 @@ class _FacilityPreorderDatePickerState
List<int> models = [];
List<int> _selectIndex = [];
List<int> _selectDates = [];
@override
Widget build(BuildContext context) {
return BeeChooseDatePicker(
height: 700.h,
onPressed: () {
Get.back(result: _selectIndex);
Get.back(result: _selectDates);
//print(_selectDates);
},
body: Container(
height: 600.h,
@ -77,17 +87,20 @@ class _FacilityPreorderDatePickerState
'todayDate': DateTime.now(),
});
if (model.success) {
models = (model.data as List<int>).toList();
models = (model.data as List<int>);
}
};
return GestureDetector(
onTap: () {
print(getNum(start)+index);
if(!models.contains(getNum(start)+index)||isPass(start.add(Duration(minutes: 30 * index)))){
//print(getNum(start)+index);
if (!models.contains(getNum(start) + index) ||
isPass(start.add(Duration(minutes: 30 * index)))) {
if (_selectIndex.contains(index)) {
_selectIndex.remove(index);
_selectDates.remove(getNum(start) + index);
} else {
_selectIndex.add(index);
_selectDates.add(getNum(start) + index);
}
}
setState(() {});
@ -98,7 +111,8 @@ class _FacilityPreorderDatePickerState
BeeCheckRadio(
value: index,
groupValue: _selectIndex,
canCheck: models.contains(getNum(start)+index)||isPass(start.add(Duration(minutes: 30 * index))),
canCheck: models.contains(getNum(start) + index) ||
isPass(start.add(Duration(minutes: 30 * index))),
),
30.wb,
Text(
@ -115,7 +129,7 @@ class _FacilityPreorderDatePickerState
.size(30.sp)
.color(Colors.black.withOpacity(0.45))
.make()
: models.contains(getNum(start)+index)
: models.contains(getNum(start) + index)
? '已被他人预约'
.text
.size(30.sp)
@ -128,14 +142,14 @@ class _FacilityPreorderDatePickerState
}
}
bool isPass(DateTime date){
if(date.hour<DateTime.now().hour||(date.minute<DateTime.now().minute&&date.hour==DateTime.now().hour)){
bool isPass(DateTime date) {
if (date.hour < DateTime.now().hour ||
(date.minute < DateTime.now().minute &&
date.hour == DateTime.now().hour)) {
return true;
}
else{
} else {
return false;
}
}
int getNum(DateTime dateTime) {

@ -1,9 +1,11 @@
import 'package:aku_new_community/widget/buttons/bee_long_button.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:bot_toast/bot_toast.dart';
import 'package:common_utils/common_utils.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:aku_new_community/base/base_style.dart';
@ -22,6 +24,9 @@ import 'package:aku_new_community/widget/buttons/bottom_button.dart';
import 'package:aku_new_community/widget/picker/bee_date_picker.dart';
import '../../../constants/saas_api.dart';
import '../../../models/facility/facility_type_model.dart';
import '../../../widget/bee_avatar_widget.dart';
import '../../../widget/bee_image_network.dart';
import '../../../widget/others/user_tool.dart';
import '../../../widget/picker/bee_choose_date_picker.dart';
import '../../../widget/picker/bee_day_picker.dart';
import '../../manager/advice/advice_house_page.dart';
@ -30,193 +35,192 @@ class FacilityPreorderPage extends StatefulWidget {
final FacilityTypeModel facilityModel;
final FacilityTypeDetailModel typeModel;
FacilityPreorderPage({Key? key, required this.facilityModel,required this.typeModel}) : super(key: key);
FacilityPreorderPage(
{Key? key, required this.facilityModel, required this.typeModel})
: super(key: key);
@override
_FacilityPreorderPageState createState() => _FacilityPreorderPageState();
}
class _FacilityPreorderPageState extends State<FacilityPreorderPage> {
DateTime? startDate;
DateTime? endDate;
List<int> dateList = [];
DateTime? date;
bool get canTap => startDate != null && endDate != null;
bool get canTap => dateList.isNotEmpty;
@override
Widget build(BuildContext context) {
final userProvider = Provider.of<UserProvider>(context);
return BeeScaffold(
title: '添加预',
title: '添加预',
systemStyle: SystemStyle.yellowBottomBar,
body: ListView(
padding: EdgeInsets.symmetric(vertical: 32.w),
padding: EdgeInsets.symmetric(vertical: 20.w),
children: [
Container(
padding: EdgeInsets.all(10.w),
color: Colors.white,
child:ListTile(
leading: Image.asset(
R.ASSETS_ICONS_HOUSE_PNG,
height: 60.w,
width: 60.w,
child: ListTile(
leading: BeeAvatarWidget(
width: 90.w,
height: 90.w,
imgs: UserTool.userProvider.userInfoModel!.imgList,
),
onTap: () => Get.to(() => AdviceHousePage()),
title: Text(S.of(context)!.tempPlotName),
subtitle: Text(userProvider.defaultHouse!.addressName),
trailing: Icon(CupertinoIcons.chevron_forward),
),
),
Text('业主房屋').pSymmetric(h: 32.w),
BeeDivider(
indent: 32.w,
endIndent: 32.w,
),
32.hb,
Text('选择设施').pSymmetric(h: 32.w),
ListTile(
leading: Image.asset(
R.ASSETS_ICONS_FACILITY_PNG,
height: 60.w,
width: 60.w,
title: Text(
UserTool.userProvider.userInfoModel!.name!,
style: TextStyle(
fontSize: 30.sp,
),
),
subtitle: Text(
'租户 ' +
userProvider.defaultHouse!.communityName +
userProvider.defaultHouseString,
style: TextStyle(
color: Colors.black.withOpacity(0.45),
fontSize: 26.sp,
),
),
trailing: Icon(
CupertinoIcons.chevron_forward,
size: 35.w,
color: Colors.black.withOpacity(0.25),
),
),
onTap: () async {await Get.to(() =>
FacilityTypeDetailPage(facilityModel: widget.facilityModel));
setState(() {});
},
title: Text(S.of(context)!.tempPlotName),
subtitle: Text(widget.typeModel.name),
trailing: Icon(CupertinoIcons.chevron_forward),
),
BeeDivider(
indent: 32.w,
endIndent: 32.w,
),
32.hb,
Text('预约时间').pSymmetric(h: 32.w),
SizedBox(
height: 120.w,
child: Row(
20.hb,
Container(
padding: EdgeInsets.all(40.w),
color: Colors.white,
child: Column(
children: [
MaterialButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
height: 120.w,
onPressed: () async {
DateTime? date = await BeeDayPicker.pick(DateTime.now());
print(date);
if (date != null) {
startDate = date;
Get.bottomSheet(FacilityPreorderDatePicker(typeModel: widget.typeModel,));
List<int> dateList=FacilityPreorderDatePicker(typeModel: widget.typeModel) as List<int>;
print(dateList);
setState(() {});
}
GestureDetector(
onTap: () async {
await Get.to(() => FacilityTypeDetailPage(
facilityModel: widget.facilityModel));
setState(() {});
},
child: Text(
startDate == null
? '请选择开始时间'
: DateUtil.formatDate(
startDate,
format: 'yyyy/MM/dd',
),
style: TextStyle(
color: ktextSubColor,
child: Material(
color: Colors.transparent,
child: Row(
children: [
SizedBox(
width: 170.w,
child: '选择设施'
.text
.size(28.sp)
.color(Colors.black.withOpacity(0.50))
.make(),
),
'${widget.typeModel.name}'
.text
.size(28.sp)
.color(Colors.black.withOpacity(0.85))
.make(),
Spacer(),
Icon(
CupertinoIcons.chevron_right,
size: 35.w,
color: Colors.black.withOpacity(0.25),
),
],
),
),
).expand(),
Icon(Icons.arrow_forward),
MaterialButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
height: 120.w,
onPressed: () async {
DateTime? date = await BeeDatePicker.pick(
startDate == null
? DateTime.now().add(Duration(minutes: 90))
: startDate!.add(Duration(minutes: 30)),
min: startDate == null
? DateTime.now().add(Duration(minutes: 60))
: startDate!.add(Duration(minutes: 30)),
max: startDate == null
? DateTime.now().add(Duration(days: 2))
: (startDate!).add(Duration(days: 2)),
mode: CupertinoDatePickerMode.dateAndTime,
);
),
30.hb,
BeeDivider(),
30.hb,
GestureDetector(
onTap: () async {
date = await BeeDayPicker.pick(DateTime.now());
if (date != null) {
endDate = date;
dateList =
await FacilityPreorderDate.choose(widget.typeModel);
dateList.sort();
setState(() {});
}
},
child: Text(
endDate == null
? '请选择结束时间'
: DateUtil.formatDate(
endDate,
format: 'yyyy-MM-dd HH:mm',
),
style: TextStyle(
color: ktextSubColor,
child: Material(
color: Colors.transparent,
child: Row(
children: [
SizedBox(
width: 170.w,
child: '预约时间'
.text
.size(28.sp)
.color(Colors.black.withOpacity(0.50))
.make(),
),
date != null
? '${DateUtil.formatDate(date, format: 'yyyy/MM/dd')} ${dateString(dateList)}'
.text
.size(28.sp)
.color(Colors.black.withOpacity(0.85))
.make()
: '请选择预约时段'
.text
.size(28.sp)
.color(Colors.black.withOpacity(0.85))
.make(),
Spacer(),
Icon(
CupertinoIcons.chevron_right,
size: 35.w,
color: Colors.black.withOpacity(0.25),
),
],
),
),
).expand(),
),
],
),
),
BeeDivider(
indent: 32.w,
endIndent: 32.w,
),
],
),
bottomNavi: BottomButton(
onPressed: canTap
? () async {
// if (dateDifferenceIsTrue) {
final cancel = BotToast.showLoading();
var model = await NetUtil().post(
SAASAPI.facilities.insert,
params: {
'estateId': userProvider.defaultHouse!.id,
'type':widget.facilityModel.type,
'facilitiesCategoryId':widget.facilityModel.id,
'facilitiesManageId':widget.typeModel.id,
'appointmentDate':1,
'appointmentPeriodList':1,
},
);
cancel();
if (model.success == true) {
BotToast.showText(text: '预约成功');
Get.back(result: true);
} else if (model.msg == '该时段已被预约') {
await Get.dialog(_hasBeenOrder());
} else {
BotToast.showText(text: '预约失败');
bottomNavi: Padding(
padding: EdgeInsets.only(
left: 32.w,
right: 32.w,
bottom: MediaQuery.of(context).padding.bottom + 32.w),
child: BeeLongButton(
onPressed: canTap
? () async {
// if (dateDifferenceIsTrue) {
final cancel = BotToast.showLoading();
var model = await NetUtil().post(
SAASAPI.facilities.insert,
params: {
'estateId': userProvider.defaultHouse!.id,
'type': widget.facilityModel.type,
'facilitiesCategoryId': widget.facilityModel.id,
'facilitiesManageId': widget.typeModel.id,
'appointmentDate': DateUtil.formatDate(date,format: 'yyyy-MM-dd HH:mm:ss'),
'appointmentPeriodList': dateList,
},
);
cancel();
if (model.success == true) {
BotToast.showText(text: '预约成功');
Get.back(result: true);
} else if (model.msg == '该时段已被预约') {
await Get.dialog(_hasBeenOrder());
} else {
BotToast.showText(text: '预约失败');
}
// } else {
// BotToast.showText(text: '预约时间必须为半小时的整数倍');
// }
}
// } else {
// BotToast.showText(text: '预约时间必须为半小时的整数倍');
// }
}
: null,
child: Text('确认提交'),
: null,
text: '确认提交',
),
),
);
}
bool get dateDifferenceIsTrue {
if (startDate != null && endDate != null) {
int _diff = endDate!.difference(startDate!).inMinutes;
if (_diff < 30) {
return false;
} else if (_diff % 30 != 0) {
return false;
}
return true;
}
return false;
}
Widget _hasBeenOrder() {
return CupertinoAlertDialog(
title: '此设施已被预约'
@ -241,8 +245,8 @@ class _FacilityPreorderPageState extends State<FacilityPreorderPage> {
'取消'.text.size(30.sp).color(ktextPrimary).isIntrinsic.make()),
CupertinoActionSheetAction(
onPressed: () {
Get.off(
() => FacilityOrderDateListPage(facilitiesId: widget.typeModel.id));
Get.off(() =>
FacilityOrderDateListPage(facilitiesId: widget.typeModel.id));
},
child:
'查看'.text.size(30.sp).color(kPrimaryColor).isIntrinsic.make()),
@ -250,3 +254,17 @@ class _FacilityPreorderPageState extends State<FacilityPreorderPage> {
);
}
}
DateTime getDate(int dateNum) {
DateTime startDate = DateTime(
DateTime.now().year, DateTime.now().month, DateTime.now().day, 0, 0, 0);
return startDate.add(Duration(minutes: 30 * (dateNum - 1)));
}
String dateString(List<int> dates){
if(dates.length==1)
return '${DateUtil.formatDate(getDate(dates.first), format: 'HH:mm')}-${DateUtil.formatDate(getDate(dates.first).add(Duration(minutes: 30)), format: 'HH:mm')}';
else{
return '${DateUtil.formatDate(getDate(dates.first), format: 'HH:mm')}-${DateUtil.formatDate(getDate(dates.first).add(Duration(minutes: 30)), format: 'HH:mm')}';
}
}

@ -82,7 +82,7 @@ class _MemberViewState extends State<MemberView> {
crossAxisCount: 4,
children: [
...model.members
.map((e) => _avatar(e.avatarImgList,Identify.values[e.identity], e.name))
.map((e) => _avatar(e.imgList,Identify.values[e.identity], e.name))
.toList()
],
shrinkWrap: true,

Loading…
Cancel
Save