This commit is contained in:
2025-04-09 13:30:50 +08:00
commit a4a995d24c
154 changed files with 9580 additions and 0 deletions

195
lib/home/home_view.dart Normal file
View File

@@ -0,0 +1,195 @@
import 'package:cashier_reserve/common/base/provider.dart';
import 'package:cashier_reserve/common/base/ui.dart';
import 'package:cashier_reserve/common/base/ui_model.dart';
import 'package:cashier_reserve/home/home_view_model.dart';
import 'package:cashier_reserve/home/order_view.dart';
import 'package:cashier_reserve/home/order_view_model.dart';
import 'package:cashier_reserve/home/reserve_view.dart';
import 'package:cashier_reserve/home/reserve_view_model.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';
class HomeView extends BaseUI {
@override
Widget buildBody(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Row(
children: <Widget>[
_buildLeftBar(context),
_buildRightContainer(context),
],
),
);
}
@override
BaseUIModel getProvider(BuildContext context, {bool listen = true}) {
return MyProvider.of<HomeViewModel>(context, listen: listen);
}
@override
String? getTitleStr(BuildContext context) {
return "";
}
@override
AppBar? getAppBar(BuildContext context) {
return null;
}
Widget _buildLeftBar(BuildContext context) {
HomeViewModel provider =
getProvider(context, listen: false) as HomeViewModel;
const double leftBarWidth = 100;
double itemHeight = (MediaQuery.of(context).size.height -
leftBarWidth -
MediaQuery.of(context).padding.top) /
provider.tabTitles.length;
return Container(
color: const Color(0xff1D2227),
width: leftBarWidth,
child: SingleChildScrollView(
child: Column(
children: _buildDestinations(
context,
provider,
leftBarWidth,
itemHeight,
),
),
),
);
}
Widget _buildRightContainer(BuildContext context) {
HomeViewModel provider =
getProvider(context, listen: false) as HomeViewModel;
return Expanded(
child: Container(
color: Colors.amber,
child: MultiProvider(
providers: [
ChangeNotifierProvider<ReserveViewModel>(
create: (_) => ReserveViewModel(),
),
ChangeNotifierProvider<OrderViewModel>(
create: (_) => OrderViewModel(),
),
],
child: PageView(
physics: const NeverScrollableScrollPhysics(),
controller: provider.pageController,
children: _buildPageViews(context, provider),
),
),
));
}
List<Widget> _buildPageViews(BuildContext context, HomeViewModel provider) {
List<Widget> items = [];
items.add(Center(
child: BaseUIController(stateWidget: ReserveView()),
));
items.add(Center(
child: BaseUIController(stateWidget: OrderView()),
));
items.add(const Center(
child: Text("打印预定"),
));
items.add(const Center(
child: Text("历史订单"),
));
items.add(const Center(
child: Text("来电"),
));
items.add(const Center(
child: Text("客户"),
));
items.add(const Center(
child: Text("消息"),
));
items.add(Center(
child: Text("更多 ${provider.version}"),
));
return items;
}
List<Widget> _buildDestinations(BuildContext context, HomeViewModel provider,
double itemWidth, double itemHeight) {
List<Widget> items = List.generate(provider.tabTitles.length, (index) {
return SizedBox(
height: itemHeight,
child: InkWell(
onTap: () {
provider.setIndex(index);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
provider.currentIndex == index
? "images/tabbar/${provider.tabIcons[index]}_select.png"
: "images/tabbar/${provider.tabIcons[index]}_normal.png",
width: 20,
height: 23,
),
const SizedBox(height: 3),
Text(
provider.tabTitles[index],
style: provider.currentIndex == index
? const TextStyle(color: Colors.white, fontSize: 12)
: const TextStyle(color: Colors.grey, fontSize: 12),
)
],
),
),
);
});
Widget topItem = SizedBox(
width: itemWidth,
height: itemWidth,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircularPercentIndicator(
animation: true,
radius: 25.0,
lineWidth: 4.0,
percent: 0.3622,
center: const Text(
"36%",
style: TextStyle(fontSize: 15, color: Colors.white),
),
progressColor: Colors.green,
),
const Text("占15空间27",
style: TextStyle(color: Colors.white, fontSize: 12)),
const Text("共130人",
style: TextStyle(color: Colors.white, fontSize: 12)),
Container(
margin: const EdgeInsets.only(top: 5),
width: 80,
height: 2,
color: Colors.grey,
)
],
),
);
// 将顶部的item放到最前面
items.insert(0, topItem);
return items;
}
}

View File

@@ -0,0 +1,147 @@
import 'package:cashier_reserve/call/call_view.dart';
import 'package:cashier_reserve/common/base/ui.dart';
import 'package:cashier_reserve/common/base/ui_model.dart';
import 'package:cashier_reserve/common/channel/channel_manager.dart';
import 'package:cashier_reserve/common/channel/model/call_status_change_model.dart';
import 'package:cashier_reserve/common/manager/app_manager.dart';
import 'package:cashier_reserve/common/manager/event_manager.dart';
import 'package:cashier_reserve/common/print/print.dart';
import 'package:cashier_reserve/common/push/push.dart';
import 'package:package_info_plus/package_info_plus.dart';
class HomeViewModel extends BaseUIModel {
int _currentIndex = 0;
int get currentIndex => _currentIndex;
final List<String> _tabTitles = [
"预定",
"订单",
"打印预定",
"历史订单",
"来电",
"客户",
"消息",
"更多"
];
final List<String> _tabIcons = [
"reserve",
"order",
"print",
"history",
"tel",
"customer",
"message",
"more"
];
List<String> get tabTitles => _tabTitles;
List<String> get tabIcons => _tabIcons;
PageController? _pageController;
PageController? get pageController => _pageController;
bool isShowCallView = false;
String version = "";
HomeViewModel() {
_pageController = PageController(initialPage: 0);
Future.delayed(const Duration(milliseconds: 700), () {
_checkLogin();
_checkAppVersion();
});
EventManager.addListener<CallStatusChangeEvent>(this, (event) {
yjPrint(
"HomeViewModel CallStatusChangeEvent state: ${event.model.state}");
/// state = IncomingNumberReceived 来电接听
/// state = OutGoing 呼出
/// state = End 通话结束
/// state = Incoming 来电
/// state = Idle 空闲
switch (event.model.state) {
case "IncomingNumberReceived":
EventManager.postEvent(CallReceivedEvent());
break;
case "OutGoing":
break;
case "Incoming":
showCallInfoView(event.model);
break;
default:
hideCallInfoView();
break;
}
});
_loadVersion();
}
@override
void dispose() {
_pageController?.dispose();
super.dispose();
}
_loadVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
version = packageInfo.version;
notifyListeners();
}
_checkLogin() {
bool flag = AppManager.isLogin();
yjPrint("is login $flag");
}
_checkAppVersion() {
AppManager.checkAppVersion();
}
void setIndex(int index) {
if (_currentIndex == index) {
return;
}
_currentIndex = index;
notifyListeners();
_pageController?.jumpToPage(index);
}
showCallInfoView(CallStatusChangeModel model) {
if (isShowCallView) {
return;
}
YJPush.presentWidget(
context!,
CallView(
statusModel: model,
onAction: (action) {
yjPrint("call view action: $action");
if (action == "accept") {
ChannelManager.acceptCall();
} else {
ChannelManager.rejectCall();
hideCallInfoView();
}
}));
isShowCallView = true;
}
hideCallInfoView() {
if (!isShowCallView) {
return;
}
Navigator.of(context!).pop();
isShowCallView = false;
ChannelManager.getCallLog("getCallLog");
}
}

29
lib/home/order_view.dart Normal file
View File

@@ -0,0 +1,29 @@
import 'package:cashier_reserve/common/base/ui.dart';
import 'package:cashier_reserve/common/base/ui_model.dart';
import 'package:cashier_reserve/home/order_view_model.dart';
import '../common/base/provider.dart';
class OrderView extends BaseUI {
@override
Widget buildBody(BuildContext context) {
return const Center(
child: Text("Order"),
);
}
@override
BaseUIModel getProvider(BuildContext context, {bool listen = true}) {
return MyProvider.of<OrderViewModel>(context, listen: listen);
}
@override
String? getTitleStr(BuildContext context) {
return "";
}
@override
AppBar? getAppBar(BuildContext context) {
return null;
}
}

View File

@@ -0,0 +1,3 @@
import 'package:cashier_reserve/common/base/ui_model.dart';
class OrderViewModel extends BaseUIModel {}

View File

@@ -0,0 +1,821 @@
import 'package:cashier_reserve/common/base/ui.dart';
import 'package:cashier_reserve/common/channel/model/call_log_model.dart';
import 'package:cashier_reserve/common/print/print.dart';
import 'package:cashier_reserve/data_model/reserve/reserve_log_model.dart';
import 'package:cashier_reserve/home/reserve_view_model.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/cupertino.dart';
class ReserveLeftContentView extends StatelessWidget {
final double contentWidth = 430;
final double inputItemHeight = 36;
final ReserveViewModel provider;
const ReserveLeftContentView({super.key, required this.provider});
@override
Widget build(BuildContext context) {
return SizedBox(
width: contentWidth,
// height: MediaQuery.of(context).size.height,
child: Stack(
children: [
_buildCallLog(context),
_buildBookingWidget(context),
],
),
);
}
Widget _buildBookingWidget(BuildContext context) {
return SizeTransition(
sizeFactor: provider.animationSizeFactor!,
axis: Axis.vertical,
axisAlignment: -1, // 设置为 -1使动画从下往上开始
child: Container(
color: const Color(0xfff5f5f5),
height: MediaQuery.of(context).size.height,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 5,
),
Container(
margin: const EdgeInsets.only(left: 15),
child: const Text(
'创建订单',
style: TextStyle(fontSize: 15, color: Color(0xff333333)),
),
),
const SizedBox(
height: 5,
),
_buildNumAndTable(context),
const SizedBox(
height: 10,
),
_buildBookingInfo(context),
const SizedBox(
height: 10,
),
_buildBookingActions(context),
],
),
),
));
}
Widget _buildCallLog(BuildContext context) {
return Column(
children: [
Expanded(
child: SizedBox(
width: contentWidth,
child: EasyRefresh(
onRefresh: () async {
provider.loadCallLog();
yjPrint("onRefresh");
},
child: ListView.builder(
itemCount: provider.callLogs?.length ?? 0,
itemBuilder: (context, index) {
return _buildCallRecordItem(context, provider.callLogs?[index]);
},
),
),
)),
Container(
padding: const EdgeInsets.fromLTRB(20, 10, 20, 15),
child: InkWell(
onTap: () {
provider.showReserveInfoView();
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: const Color(0xff318AFE),
),
width: 300,
height: inputItemHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset(
'images/reserve/create.png',
width: 20,
height: 20,
),
const SizedBox(
width: 5,
),
const Text(
"创建订单",
style: TextStyle(color: Colors.white, fontSize: 14),
),
],
),
),
),
)
],
);
}
/// _buildCallRecordItem 通话记录item
Widget _buildCallRecordItem(BuildContext context, CallLogModel? model) {
ReserveLogModel? reserveLogModel =
provider.getReserveLogModel(model?.number ?? "");
return Container(
padding: const EdgeInsets.fromLTRB(15, 15, 15, 5),
child: Column(
children: [
Row(
children: [
Column(
children: [
Image.asset(
(model?.type ?? 0) == 3
? "images/reserve/phone_fail.png"
: "images/reserve/phone_suc.png",
width: 24,
height: 24,
),
const SizedBox(height: 2),
Text(
model?.time ?? "",
style:
const TextStyle(color: Color(0xff999999), fontSize: 12),
),
],
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
model?.name ?? "未知电话",
style:
const TextStyle(color: Color(0xff333333), fontSize: 14),
),
const SizedBox(height: 5),
Row(
children: [
Text(
model?.number ?? "",
style: const TextStyle(
color: Color(0xff333333), fontSize: 14),
),
const SizedBox(
width: 15,
),
Text(
"已消费${reserveLogModel?.consumeOrders ?? '0'}",
style: const TextStyle(
color: Color(0xff333333), fontSize: 14),
),
const SizedBox(
width: 15,
),
Text(
"已撤${reserveLogModel?.cancelOrders ?? '0'}",
style: const TextStyle(
color: Color(0xff333333), fontSize: 14),
),
],
),
],
),
const Expanded(child: SizedBox()),
InkWell(
onTap: () {
provider.execCallLog(model);
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border:
Border.all(color: const Color(0xff318AFE), width: 1),
),
padding: const EdgeInsets.fromLTRB(20, 7, 20, 7),
child: const Text(
"处理",
style: TextStyle(color: Color(0xff318AFE), fontSize: 14),
),
),
)
],
),
const SizedBox(
height: 10,
),
Container(
height: 1,
color: const Color(0xffededed),
),
],
),
);
}
/// _buildNumAndTable 就餐人数和桌台
Widget _buildNumAndTable(BuildContext context) {
return Container(
color: Colors.white,
padding: const EdgeInsets.fromLTRB(15, 15, 15, 15),
child: Column(
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: const Color(0xffededed), width: 1),
),
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
height: inputItemHeight,
child: Row(
children: [
const Text(
"就餐人数",
style: TextStyle(color: Color(0xff333333), fontSize: 14),
),
const SizedBox(
width: 10,
),
Expanded(
child: TextField(
controller: provider.bookingNumController,
decoration: InputDecoration(
hintText: "请输入就餐人数",
hintStyle: const TextStyle(
fontSize: 14,
color: Color(0xff999999),
),
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(5),
),
),
keyboardType: TextInputType.number,
textAlign: TextAlign.right,
),
),
const SizedBox(
width: 10,
),
const Text(
"",
style: TextStyle(color: Color(0xff333333), fontSize: 14),
),
],
),
),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"请选择桌台",
style: TextStyle(color: Color(0xff333333), fontSize: 14),
),
Text(provider.showTableName,
style:
const TextStyle(color: Color(0xff333333), fontSize: 14)),
],
),
const SizedBox(
height: 5,
),
],
),
);
}
/// _buildBookingInfo 预订信息
Widget _buildBookingInfo(BuildContext context) {
return Container(
color: Colors.white,
padding: const EdgeInsets.fromLTRB(15, 15, 15, 15),
child: Column(
children: [
_buildBookingPhone(context),
const SizedBox(
height: 5,
),
_buildBookingName(context),
const SizedBox(
height: 5,
),
_buildBookingTimeAndType(context),
const SizedBox(
height: 5,
),
_buildBookingAttribute(context),
const SizedBox(
height: 5,
),
_buildBookingTableNum(context),
const SizedBox(
height: 5,
),
_buildBookingStandard(context),
const SizedBox(
height: 5,
),
_buildBookingRemark(context),
],
),
);
}
/// _buildBookingActions 预订操作
Widget _buildBookingActions(BuildContext context) {
return Container(
color: Colors.white,
padding: const EdgeInsets.fromLTRB(15, 5, 15, 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildBookingActionBtn(context, "预约", () {
provider.commitReserveInfo();
}),
_buildBookingActionBtn(context, "预约并短信", () {
provider.commitReserveInfo(sendSms: true);
}),
_buildBookingActionBtn(context, "发路线", () {}),
_buildBookingActionBtn(context, "取消", () {
provider.hideReserveInfoView();
}),
],
),
);
}
/// _buildBookingPhone 预订电话
Widget _buildBookingPhone(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: const Color(0xffededed), width: 1),
),
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
height: inputItemHeight,
child: Row(
children: [
const Text(
"电话",
style: TextStyle(color: Color(0xff333333), fontSize: 14),
),
const SizedBox(
width: 10,
),
Expanded(
child: TextField(
controller: provider.bookingPhoneController,
decoration: InputDecoration(
hintText: "请输入电话",
hintStyle: const TextStyle(
fontSize: 14,
color: Color(0xff999999),
),
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(5),
),
),
keyboardType: TextInputType.number,
textAlign: TextAlign.left,
),
),
const SizedBox(
width: 10,
),
],
),
);
}
/// _buildBookingName 预订人
Widget _buildBookingName(BuildContext context) {
return SizedBox(
width: contentWidth - 30,
child: Row(
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: const Color(0xffededed), width: 1),
),
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
height: inputItemHeight,
width: contentWidth - 160,
child: Row(
children: [
const Text(
"姓名",
style: TextStyle(color: Color(0xff333333), fontSize: 14),
),
const SizedBox(
width: 10,
),
Expanded(
child: TextField(
controller: provider.bookingNameController,
decoration: InputDecoration(
hintText: "请输入姓名",
hintStyle: const TextStyle(
fontSize: 14,
color: Color(0xff999999),
),
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(5),
),
),
textAlign: TextAlign.left,
),
),
const SizedBox(
width: 10,
),
],
),
),
const SizedBox(
width: 10,
),
// 先生、女士
SizedBox(
width: 120,
child: CupertinoSegmentedControl(
//子标签
children: {
1: Container(
width: 60,
height: inputItemHeight,
alignment: Alignment.center,
child: const Text(
"先生",
style: TextStyle(
fontSize: 14,
// color: Color(0xff333333),
decoration: TextDecoration.none),
),
),
2: Container(
width: 60,
height: inputItemHeight,
alignment: Alignment.center,
child: const Text(
"女士",
style: TextStyle(
fontSize: 14,
// color: Color(0xff333333),
decoration: TextDecoration.none),
),
),
},
//当前选中的索引
groupValue: provider.bookingGender,
//点击回调
onValueChanged: (int index) {
provider.updateBookingGender(index);
},
selectedColor: Colors.blue,
borderColor: Colors.blue,
unselectedColor: Colors.white,
),
),
],
),
);
}
/// _buildBookingTimeAndType 预订时间和类型
Widget _buildBookingTimeAndType(BuildContext context) {
final itemWidth = (contentWidth - 40) / 2;
return Row(
children: [
Container(
width: itemWidth,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: const Color(0xffededed), width: 1),
),
height: inputItemHeight,
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text("时间",
style: TextStyle(color: Color(0xff666666), fontSize: 14)),
InkWell(
onTap: () async {
TimeOfDay? t = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
yjPrint(t);
provider.updateBookingTime(t?.hour ?? 0, t?.minute ?? 0);
},
child: (provider.bookingSelectedTime == "")
? const Text(
"请选择时间",
style:
TextStyle(color: Color(0xff999999), fontSize: 14),
)
: Text(
provider.bookingSelectedTime,
style: const TextStyle(
color: Color(0xff333333), fontSize: 14),
),
)
],
),
),
const SizedBox(
width: 10,
),
Container(
width: itemWidth,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: const Color(0xffededed), width: 1),
),
height: inputItemHeight,
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
child: Row(
children: [
const Text("类型",
style: TextStyle(color: Color(0xff666666), fontSize: 14)),
const SizedBox(
width: 10,
),
Expanded(
child: TextField(
controller: provider.bookingTypeController,
decoration: InputDecoration(
hintText: "请输入类型",
hintStyle: const TextStyle(
fontSize: 14,
color: Color(0xff999999),
),
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(5),
),
),
textAlign: TextAlign.right,
),
),
const SizedBox(
width: 10,
),
],
),
),
],
);
}
/// _buildBookingAttribute 预订属性
Widget _buildBookingAttribute(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: const Color(0xffededed), width: 1),
),
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
height: inputItemHeight,
width: contentWidth - 30,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildBookingAttrItem(context, "重点关注", provider.bookingFocus,
(value) {
provider.updateBookingAttr("focus", value);
}),
_buildBookingAttrItem(context, "接收营销短信", provider.bookingSms,
(value) {
provider.updateBookingAttr("sms", value);
}),
],
),
);
}
Widget _buildBookingAttrItem(BuildContext context, String name, bool isOpen,
Function(bool) onChanged) {
return Row(
children: [
Text(name,
style: const TextStyle(color: Color(0xff999999), fontSize: 14)),
const SizedBox(width: 5),
Transform.scale(
scale: 0.8, // 调整这个值来改变大小大于1放大小于1缩小
child: CupertinoSwitch(
value: isOpen, onChanged: onChanged, activeColor: Colors.blue),
),
],
);
}
/// _buildBookingTableNum 预订桌台数量
Widget _buildBookingTableNum(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: const Color(0xffededed), width: 1),
),
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
height: inputItemHeight,
// width: contentWidth - 30,
child: Row(
children: [
const Text(
"摆台桌数(桌)",
style: TextStyle(color: Color(0xff333333), fontSize: 14),
),
const SizedBox(
width: 10,
),
Expanded(
child: TextField(
controller: provider.bookingTableNumController,
decoration: InputDecoration(
hintText: "请输入台桌数",
hintStyle: const TextStyle(
fontSize: 14,
color: Color(0xff999999),
),
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(5),
),
),
textAlign: TextAlign.left,
),
),
const SizedBox(
width: 10,
),
],
),
);
}
Widget _buildBookingStandard(BuildContext context) {
final itemWidth = (contentWidth - 40) / 2;
return Row(
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: const Color(0xffededed), width: 1),
),
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
width: itemWidth,
height: inputItemHeight,
child: Row(
children: [
const Text(
"餐标",
style: TextStyle(color: Color(0xff333333), fontSize: 14),
),
const SizedBox(
width: 10,
),
Expanded(
child: TextField(
controller: provider.bookingStandardController,
decoration: InputDecoration(
hintText: "请输入标准",
hintStyle: const TextStyle(
fontSize: 14,
color: Color(0xff999999),
),
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(5),
),
),
textAlign: TextAlign.left,
),
),
const SizedBox(
width: 10,
),
],
),
),
const SizedBox(
width: 10,
),
SizedBox(
height: inputItemHeight,
width: itemWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Radio<String>(
value: "table",
groupValue: provider.bookingStandardType,
onChanged: (String? value) {
provider.updateBookingStandard(value ?? "table");
},
),
const Text(
'元/桌',
style: TextStyle(fontSize: 12, color: Color(0xff666666)),
),
Radio<String>(
value: "person",
groupValue: provider.bookingStandardType,
onChanged: (String? value) {
provider.updateBookingStandard(value ?? "person");
},
),
const Text(
'元/人',
style: TextStyle(fontSize: 12, color: Color(0xff666666)),
),
],
),
)
],
);
}
/// _buildBookingRemark 备注
Widget _buildBookingRemark(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: const Color(0xffededed), width: 1),
),
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
height: inputItemHeight,
child: Row(
children: [
const Text(
"备注",
style: TextStyle(color: Color(0xff333333), fontSize: 14),
),
const SizedBox(
width: 10,
),
Expanded(
child: TextField(
controller: provider.bookingRemarkController,
decoration: InputDecoration(
hintText: "请输入备注",
hintStyle: const TextStyle(
fontSize: 14,
color: Color(0xff999999),
),
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(5),
),
),
textAlign: TextAlign.left,
),
),
const SizedBox(
width: 10,
),
],
),
);
}
Widget _buildBookingActionBtn(
BuildContext context, String title, Function() onTap) {
return InkWell(
onTap: onTap,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.blue,
),
padding: const EdgeInsets.fromLTRB(20, 5, 20, 5),
child: Text(
title,
style: const TextStyle(color: Colors.white, fontSize: 14),
),
),
);
}
}

View File

@@ -0,0 +1,98 @@
import 'package:cashier_reserve/common/base/ui.dart';
import 'package:cashier_reserve/common/print/print.dart';
import 'package:cashier_reserve/data_model/reserve/table_area_model.dart';
import 'package:cashier_reserve/home/reserve_view_model.dart';
import 'reserve_right_table_list.dart';
class ReserveRightContentView extends StatelessWidget {
final ReserveViewModel provider;
final TabController? tabController;
const ReserveRightContentView(
{super.key, required this.provider, this.tabController});
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTabBar(context),
_buildTableListWidget(context),
],
),
);
}
Widget _buildTabBar(BuildContext context) {
if ((provider.tableAreaList?.length ?? 0) == 0) {
return Container();
}
return TabBar(
tabs:
provider.tableAreaList!.map((e) => Tab(text: e?.name ?? '')).toList(),
controller: tabController,
isScrollable: true,
labelColor: Colors.blue,
unselectedLabelColor: Colors.grey,
indicatorColor: Colors.blue,
indicatorSize: TabBarIndicatorSize.label,
indicatorWeight: 2,
labelPadding: const EdgeInsets.only(left: 10, right: 10),
onTap: (index) {
provider.pageController.jumpToPage(index);
},
);
}
Widget _buildTableListWidget(BuildContext context) {
if ((provider.tableAreaList?.length ?? 0) == 0) {
return Container();
}
return Expanded(
child: Container(
padding: const EdgeInsets.only(left: 15, right: 15),
color: const Color(0xFFF5F5F5),
child: PageView(
controller: provider.pageController,
physics: const NeverScrollableScrollPhysics(),
children: _buildTableList(context),
),
),
);
}
List<Widget> _buildTableList(BuildContext context) {
List<Widget> list = [];
for (int i = 0; i < provider.tableAreaList!.length; i++) {
List<TableAreaModel?> areas = [];
if (i == 0) {
/// 排除第一个全部
areas.addAll(provider.tableAreaList!.sublist(1));
} else {
areas.add(provider.tableAreaList![i]);
}
list.add(
ReserveRightTableList(
areas: areas,
getAreaTableListFunc: (areaId) {
return provider.tableMap[areaId] ?? [];
},
tableClickFunc: (table) {
yjPrint("table: ${table.name}");
provider.clickTable(table);
},
),
);
}
return list;
}
}

View File

@@ -0,0 +1,161 @@
import 'package:cashier_reserve/common/base/ui.dart';
import 'package:cashier_reserve/data_model/reserve/table_area_model.dart';
import 'package:cashier_reserve/data_model/reserve/table_model.dart';
typedef GetAreaTableListFunc = List<TableModel?> Function(String areaId);
typedef TableClickFunc = void Function(TableModel table);
class ReserveRightTableList extends StatelessWidget {
final List<TableAreaModel?> areas;
final GetAreaTableListFunc getAreaTableListFunc;
final TableClickFunc? tableClickFunc;
const ReserveRightTableList({
super.key,
required this.areas,
required this.getAreaTableListFunc,
this.tableClickFunc,
});
@override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: _buildTableAreaItem, itemCount: areas.length);
}
Widget _buildTableAreaItem(BuildContext context, int index) {
TableAreaModel? area = areas[index];
List<TableModel?> tables = getAreaTableListFunc(area?.id.toString() ?? "");
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTableAreaTitle(context, area, tables),
const SizedBox(height: 5),
_buildTableList(context, tables),
const SizedBox(height: 10),
],
);
}
Widget _buildTableAreaTitle(
BuildContext context, TableAreaModel? area, List<TableModel?> tables) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 2,
height: 15,
color: Colors.blue,
),
const SizedBox(width: 10),
Text(
"${area?.name ?? ""}${tables.length}",
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
],
);
}
Widget _buildTableList(BuildContext context, List<TableModel?> tables) {
return Wrap(
children: tables.map((e) => _buildTableItem(context, e)).toList(),
);
}
Widget _buildTableItem(BuildContext context, TableModel? table) {
bool isBooking = table?.bookingInfo != null;
const itemNormalTextStyle = TextStyle(
color: Color(0xff333333),
fontSize: 12,
);
return GestureDetector(
onTap: () {
if (tableClickFunc != null) {
tableClickFunc!(table!);
}
},
child: Container(
width: 103,
height: 129,
margin: const EdgeInsets.fromLTRB(0, 0, 10, 0),
decoration: BoxDecoration(
color: isBooking ? const Color(0xffFFF4DF) : Colors.white,
borderRadius: BorderRadius.circular(5),
),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(10),
child: Column(
children: [
Text(
table?.name ?? "",
style: const TextStyle(
color: Color(0xff333333),
fontSize: 16,
),
),
const SizedBox(height: 3),
if (!isBooking)
Text(
"${table?.maxCapacity ?? ""}",
style: itemNormalTextStyle,
),
if (isBooking)
Text(
"${(table?.bookingInfo?.createUserName?.length ?? 0) > 3 ? '${table?.bookingInfo?.createUserName?.substring(0, 3)}...' : table?.bookingInfo?.createUserName} 订",
style: itemNormalTextStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (isBooking) const SizedBox(height: 3),
if (isBooking)
Text(
"${table?.bookingInfo?.bookingPerson ?? ""}(${table?.bookingInfo?.gender == 1 ? '先生' : '女士'})",
style: itemNormalTextStyle,
),
if (isBooking) const SizedBox(height: 3),
if (isBooking)
Text(
"${table?.bookingInfo?.dinerNum ?? ""}人/${table?.bookingInfo?.phoneNumber?.substring(7) ?? ""}",
style: itemNormalTextStyle,
),
],
),
),
Expanded(child: Container()),
if (isBooking)
Container(
width: double.infinity,
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Color(0xffF8AD13),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(5),
bottomRight: Radius.circular(5),
),
),
child: const Row(
children: [
SizedBox(width: 5),
Text(
"",
style: TextStyle(
color: Colors.white,
fontSize: 11,
),
),
],
),
),
],
),
),
);
}
}

325
lib/home/reserve_view.dart Normal file
View File

@@ -0,0 +1,325 @@
import 'package:cashier_reserve/common/base/ui.dart';
import 'package:cashier_reserve/common/base/ui_model.dart';
import 'package:cashier_reserve/home/reserve_left_content_view.dart';
import 'package:cashier_reserve/home/reserve_right_content_view.dart';
import 'package:cashier_reserve/home/reserve_view_model.dart';
import '../common/base/provider.dart';
class ReserveView extends BaseUI with TickerProviderStateMixin {
TabController? tabController;
late AnimationController animationController;
late Animation<double> animationSizeFactor;
@override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
animationSizeFactor = Tween<double>(begin: 0, end: 1).animate(
CurveTween(curve: Curves.easeInOut)
.chain(CurveTween(curve: Curves.decelerate))
.animate(animationController),
);
}
@override
void dispose() {
tabController?.dispose();
animationController.dispose();
animationSizeFactor.removeListener(() {});
super.dispose();
}
@override
Widget buildBody(BuildContext context) {
ReserveViewModel provider =
getProvider(context, listen: false) as ReserveViewModel;
provider.setAnimationController(animationController, animationSizeFactor);
return SizedBox(
width: double.infinity,
child: Column(
children: [
_buildTopDateBar(context, provider),
Expanded(child: _buildContentView(context, provider)),
],
),
);
}
@override
BaseUIModel getProvider(BuildContext context, {bool listen = true}) {
return MyProvider.of<ReserveViewModel>(context, listen: listen);
}
@override
String? getTitleStr(BuildContext context) {
return "";
}
@override
AppBar? getAppBar(BuildContext context) {
return null;
}
Widget _buildTopDateBar(BuildContext context, ReserveViewModel provider) {
return Padding(
padding: const EdgeInsets.fromLTRB(15, 5, 15, 5),
child: Row(
children: [
_buildDateItem(context, provider),
_buildDateSelectContent(context, provider),
const SizedBox(
width: 15,
),
InkWell(
onTap: () {
provider.testCallIncoming();
},
child: SizedBox(
width: 40,
height: 40,
child: Center(
child: Image.asset(
"images/reserve/lock.png",
width: 24,
height: 24,
),
),
),
),
const SizedBox(
width: 5,
),
InkWell(
onTap: () { },
child: SizedBox(
width: 40,
height: 40,
child: Center(
child: Image.asset(
"images/reserve/phone.png",
width: 24,
height: 24,
),
),
),
),
const SizedBox(
width: 5,
),
InkWell(
onTap: () {
provider.reloadPageData();
},
child: const SizedBox(
width: 40,
height: 40,
child: Center(
child: Text(
"刷新",
style: TextStyle(fontSize: 15, color: Colors.blue),
),
),
),
),
],
),
);
}
Widget _buildContentView(BuildContext context, ReserveViewModel provider) {
if ((provider.tableAreaList?.length ?? 0) > 0) {
if (tabController == null) {
tabController =
TabController(vsync: this, length: provider.tableAreaList!.length);
} else {
if (tabController!.length != provider.tableAreaList!.length) {
tabController!.dispose();
tabController = TabController(
vsync: this, length: provider.tableAreaList!.length);
}
}
}
return Row(
children: [
ReserveLeftContentView(
provider: provider,
),
Expanded(
child: ReserveRightContentView(
provider: provider,
tabController: tabController,
),
),
],
);
}
Widget _buildDateItem(BuildContext context, ReserveViewModel provider) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(color: const Color(0xffafafaf), width: 1),
),
height: 40,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
child: Row(
children: [
Image.asset(
"images/reserve/date.png",
width: 24,
height: 24,
),
const SizedBox(width: 7),
Text(
provider.getCurrentDate(),
style: const TextStyle(color: Color(0xff333333), fontSize: 14),
)
],
),
);
}
// Widget _buildCallRecordListWidget(BuildContext context, ReserveViewModel provider) {
// return ListView.builder(
// itemCount: provider.callRecordList.length,
// itemBuilder: (context, index) {
// return _buildCallRecordItem(context, provider.callRecordList[index]);
// },
// );
// }
Widget _buildDateSelectContent(
BuildContext context, ReserveViewModel provider) {
return Row(
children: [
_buildDateSelectItem(
context,
provider,
),
_buildDateSelectItem(
context,
provider,
offset: 1,
),
_buildDateSelectItem(
context,
provider,
offset: 2,
),
],
);
}
Widget _buildDateSelectItem(
BuildContext context,
ReserveViewModel provider, {
int offset = 0,
}) {
bool isSelect = provider.selectedDateIndex ~/ 2 == offset;
bool firstBtnSelect = provider.selectedDateIndex == (offset * 2);
bool secondBtnSelect = provider.selectedDateIndex == (offset * 2 + 1);
const double itemHeight = 45;
const double btnWidth = 60;
return Container(
height: itemHeight,
margin: const EdgeInsets.only(left: 15),
padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
border: Border.all(
color:
isSelect ? const Color(0xff318AFE) : const Color(0xffafafaf),
width: 1),
),
child: Row(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
provider.getOffsetDay(offset),
style: TextStyle(
color: isSelect
? const Color(0xff6A8DC6)
: const Color(0xff333333),
fontSize: 13,
),
),
Text(
provider.getOffsetWeekday(offset),
style: TextStyle(
color: isSelect
? const Color(0xff6A8DC6)
: const Color(0xff333333),
fontSize: 13,
),
),
],
),
const SizedBox(width: 10),
Container(
width: 1,
height: itemHeight,
color: const Color(0xffafafaf),
),
InkWell(
onTap: () {
provider.setSelectedDateIndex(offset * 2);
},
child: Container(
decoration: BoxDecoration(
color:
firstBtnSelect ? const Color(0xff318AFE) : Colors.white,
),
width: btnWidth,
child: Center(
child: Text(
"午餐",
style: TextStyle(
fontSize: 16,
color: firstBtnSelect
? Colors.white
: const Color(0xff333333)),
),
),
),
),
Container(
width: 1,
height: itemHeight,
color: const Color(0xffafafaf),
),
InkWell(
onTap: () {
provider.setSelectedDateIndex(offset * 2 + 1);
},
child: Container(
decoration: BoxDecoration(
color:
secondBtnSelect ? const Color(0xff318AFE) : Colors.white,
),
width: btnWidth,
child: Center(
child: Text(
"晚餐",
style: TextStyle(
fontSize: 16,
color: secondBtnSelect
? Colors.white
: const Color(0xff333333)),
),
),
),
),
],
));
}
}

View File

@@ -0,0 +1,470 @@
import 'dart:async';
import 'package:cashier_reserve/common/base/ui_model.dart';
import 'package:cashier_reserve/common/channel/model/call_log_model.dart';
import 'package:cashier_reserve/common/channel/channel_manager.dart';
import 'package:cashier_reserve/common/channel/model/call_status_change_model.dart';
import 'package:cashier_reserve/common/manager/app_manager.dart';
import 'package:cashier_reserve/common/manager/event_manager.dart';
import 'package:cashier_reserve/common/print/print.dart';
import 'package:cashier_reserve/common/utils/utils.dart';
import 'package:cashier_reserve/data_model/reserve/reserve_log_model.dart';
import 'package:cashier_reserve/data_model/reserve/table_area_model.dart';
import 'package:cashier_reserve/data_model/reserve/table_model.dart';
import 'package:cashier_reserve/model/reserve_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:url_launcher/url_launcher.dart';
class ReserveViewModel extends BaseUIModel {
Map<int, String> weekdayMap = {
1: "星期一",
2: "星期二",
3: "星期三",
4: "星期四",
5: "星期五",
6: "星期六",
7: "星期日",
};
Map<int, String> dayInfoMap = {
0: "",
1: "",
2: "",
};
PageController pageController = PageController();
AnimationController? _animationController;
Animation<double>? _animationSizeFactor;
Animation<double>? get animationSizeFactor => _animationSizeFactor;
DateTime now = DateTime.now();
int selectedDateIndex = 0;
String selectedDate = "";
String bookingType = "lunch";
List<TableAreaModel?>? _tableAreaList;
List<TableAreaModel?>? get tableAreaList => _tableAreaList;
Map<num, TableAreaModel?> _tableAreaMap = {};
List<TableModel?>? _tableList;
List<TableModel?>? get tableList => _tableList;
Map<String, List<TableModel?>> tableMap = {};
List<CallLogModel?>? callLogs = [];
Map<String, ReserveLogModel?>? _reserveLogMap;
bool _isShowReserveInfoView = false;
/// bookingGender 预订人性别 1: 男 2: 女
int bookingGender = 1;
/// bookingNumController 就餐人数
TextEditingController bookingNumController = TextEditingController();
/// bookingPhoneController 联系电话
TextEditingController bookingPhoneController = TextEditingController();
/// bookingNameController 预订人姓名
TextEditingController bookingNameController = TextEditingController();
/// bookingTypeController 预订类型
TextEditingController bookingTypeController = TextEditingController();
/// bookingTableNumController 预订台桌数量
TextEditingController bookingTableNumController = TextEditingController();
/// bookingStandardController 预定餐标
TextEditingController bookingStandardController = TextEditingController();
/// bookingRemarkController 备注
TextEditingController bookingRemarkController = TextEditingController();
/// bookingSelectedTime 预订时间
String bookingSelectedTime = "";
/// bookingFocus 重点关注
bool bookingFocus = false;
/// bookingSms 短信通知
bool bookingSms = false;
/// bookingStandardType 餐标类型
String bookingStandardType = "table";
String showTableName = "";
TableModel? selectedTable;
Timer? periodicTimer;
ReserveViewModel() {
selectedDate = "${now.year}-${now.month}-${now.day}";
EventManager.addListener<GetCallLogEvent>(this, (event) {
if (event.isSuccess) {
if (!event.isSuccess) {
return;
}
callLogs = event.callLogs;
notifyListeners();
loadUserReserveLog();
}
});
loadCallLog();
loadTableAreaList();
loadAreaTableList(0);
periodicTimer = Timer.periodic(const Duration(seconds: 30), (timer) {
loadAreaTableList(0);
});
}
@override
void dispose() {
pageController.dispose();
bookingNumController.dispose();
bookingPhoneController.dispose();
bookingNameController.dispose();
bookingTypeController.dispose();
bookingTableNumController.dispose();
bookingStandardController.dispose();
bookingRemarkController.dispose();
periodicTimer?.cancel();
super.dispose();
}
reloadPageData() {
loadCallLog();
loadTableAreaList();
loadAreaTableList(0);
loadReserveSms();
}
void testCallIncoming() {
CallStatusChangeModel model = CallStatusChangeModel(
state: "Incoming",
number: "123456789",
name: "测试",
region: "陕西省 西安市",
);
EventManager.postEvent(CallStatusChangeEvent(model: model));
}
void loadCallLog() {
ChannelManager.getCallLog("getCallLog");
}
void loadUserReserveLog() async {
List<String> phoneNos = [];
for (var item in callLogs!) {
phoneNos.add(item!.number ?? '');
}
_reserveLogMap = await ReserveModel.getUserReserveLog(phoneNos);
notifyListeners();
}
void loadTableAreaList() async {
final r = await ReserveModel.getShopTableAreaList();
_tableAreaList = r;
_tableAreaList ??= [];
_tableAreaMap = {};
for (var item in _tableAreaList!) {
_tableAreaMap[item!.id!] = item;
}
_tableAreaList!.insert(0, TableAreaModel(id: 0, name: "全部"));
notifyListeners();
}
void loadAreaTableList(int areaId) async {
final r =
await ReserveModel.getAreaTableList(areaId, selectedDate, bookingType);
_tableList = r;
_tableList ??= [];
tableMap = {};
tableMap["0"] = _tableList!;
for (var item in _tableList!) {
String areaId = item!.areaId.toString();
if (tableMap[areaId] == null) {
tableMap[areaId] = [];
}
tableMap[areaId]!.add(item);
}
notifyListeners();
}
loadReserveSms() async {
AppManager.loadReserveSms();
}
void commitReserveInfo({bool sendSms = false}) async {
if (!_checkReserveInfo()) {
return;
}
Map<String, dynamic> params = {
"shopTableId": selectedTable!.id,
"bookingDate": selectedDate,
"bookingType": bookingType,
"dinerNum": bookingNumController.text,
"phoneNumber": bookingPhoneController.text,
"bookingPerson": bookingNameController.text,
"gender": bookingGender,
"bookingTime": '$selectedDate $bookingSelectedTime:00',
"diningType": bookingTypeController.text,
"focus": bookingFocus ? 1 : 0,
"receiveMarketingSms": bookingSms ? 1 : 0,
"bookingTableNum": bookingTableNumController.text,
"diningStandardPrice": bookingStandardController.text,
"diningStandardUnit": bookingStandardType,
"remark": bookingRemarkController.text,
};
await ReserveModel.commitReserveInfo(params);
Utils.toast("预定成功", context);
hideReserveInfoView();
loadAreaTableList(0);
if (sendSms) {
String smsContent = await AppManager.loadReserveSms();
if (smsContent.isNotEmpty) {
yjPrint("send sms: $smsContent, phone: ${bookingPhoneController.text}");
goSendSms(phone: bookingPhoneController.text, message: smsContent);
}
}
}
goSendSms({required String phone, required String message}) async {
final Uri smsUri = Uri(
scheme: 'sms',
path: phone,
queryParameters: {
'body': message,
},
);
if (await canLaunchUrl(smsUri)) {
await launchUrl(smsUri);
} else {
// 如果无法启动短信发送,可在此处添加相应的提示逻辑,比如打印错误信息等
yjPrint('Could not launch SMS');
}
}
void setSelectedDateIndex(int index) {
selectedDateIndex = index;
DateTime offsetDay = now.add(Duration(days: index ~/ 2));
selectedDate = "${offsetDay.year}-${offsetDay.month}-${offsetDay.day}";
bookingType = index % 2 == 0 ? "lunch" : "dinner";
notifyListeners();
loadAreaTableList(0);
}
void setAnimationController(
AnimationController controller, Animation<double> sizeFactor) {
_animationController = controller;
_animationSizeFactor = sizeFactor;
}
ReserveLogModel? getReserveLogModel(String phone) {
return _reserveLogMap?[phone];
}
showReserveInfoView() {
_resetReserveData();
_isShowReserveInfoView = true;
_animationController?.forward();
notifyListeners();
}
hideReserveInfoView() {
_isShowReserveInfoView = false;
_animationController?.reverse();
}
execCallLog(CallLogModel? callLog) {
showReserveInfoView();
bookingPhoneController.text = callLog?.number ?? "";
bookingNameController.text = callLog?.name ?? "";
notifyListeners();
}
updateBookingTime(int hour, int minute) {
bookingSelectedTime =
"${hour.toString().padLeft(2, '0')}:${minute.toString().padLeft(2, '0')}";
notifyListeners();
}
updateBookingGender(int gender) {
if (bookingGender == gender) {
return;
}
bookingGender = gender;
notifyListeners();
}
updateBookingAttr(String key, bool val) {
if (key == "focus") {
bookingFocus = val;
} else if (key == "sms") {
bookingSms = val;
}
notifyListeners();
}
updateBookingStandard(String standard) {
bookingStandardType = standard;
notifyListeners();
}
clickTable(TableModel table) {
if (_isShowReserveInfoView) {
if (table.bookingInfo != null) {
Utils.toast("当前台桌已预定", context);
return;
}
selectedTable = table;
TableAreaModel? area = _tableAreaMap[table.areaId!];
showTableName = "${area?.name ?? ""} - ${table.name}";
notifyListeners();
return;
}
if (table.bookingInfo == null) {
return;
}
_changeReserveStatus(table);
}
_resetReserveData() {
bookingGender = 1;
bookingNumController.text = "";
bookingPhoneController.text = "";
bookingNameController.text = "";
bookingTypeController.text = "";
bookingTableNumController.text = "";
bookingStandardController.text = "";
bookingRemarkController.text = "";
bookingSelectedTime = "";
bookingFocus = false;
bookingSms = false;
bookingStandardType = "table";
selectedTable = null;
showTableName = "";
}
_changeReserveStatus(TableModel table) async {
var result = await showCupertinoModalPopup(
context: context!,
builder: (context) {
return CupertinoActionSheet(
title: const Text('预定信息'),
actions: <Widget>[
CupertinoActionSheetAction(
onPressed: () {
Navigator.of(context).pop('arrive');
},
isDefaultAction: true,
child: const Text('到店'),
),
CupertinoActionSheetAction(
child: const Text('取消预定'),
onPressed: () {
Navigator.of(context).pop('cancelBooking');
},
// isDestructiveAction: true,
),
],
cancelButton: CupertinoActionSheetAction(
child: const Text('取消'),
onPressed: () {
Navigator.of(context).pop('cancel');
},
),
);
});
yjPrint('$result');
if (result == 'arrive') {
await ReserveModel.updateReserveStatus(table.bookingInfo?.id ?? 0, 10);
Utils.toast("到店成功", context);
loadAreaTableList(0);
} else if (result == 'cancelBooking') {
ReserveModel.updateReserveStatus(table.bookingInfo?.id ?? 0, -1);
Utils.toast("取消成功", context);
loadAreaTableList(0);
}
}
bool _checkReserveInfo() {
if (selectedTable == null) {
Utils.toast("请选择台桌", context);
return false;
}
if (bookingNumController.text.isEmpty) {
Utils.toast("请输入就餐人数", context);
return false;
}
if (bookingPhoneController.text.isEmpty) {
Utils.toast("请输入联系电话", context);
return false;
}
if (bookingNameController.text.isEmpty) {
Utils.toast("请输入预订人姓名", context);
return false;
}
if (bookingSelectedTime.isEmpty) {
Utils.toast("请选择预订时间", context);
return false;
}
if (bookingTypeController.text.isEmpty) {
Utils.toast("请输入预订类型", context);
return false;
}
return true;
}
String getCurrentDate() {
return "${now.year}/${now.month}/${now.day}";
}
String getOffsetDay(int offset) {
DateTime offsetDay = now.add(Duration(days: offset));
return "${dayInfoMap[offset]}/${offsetDay.day}";
}
String getOffsetWeekday(int offset) {
DateTime offsetDay = now.add(Duration(days: offset));
return weekdayMap[offsetDay.weekday] ?? "";
}
}