first
This commit is contained in:
168
lib/call/call_view.dart
Normal file
168
lib/call/call_view.dart
Normal file
@@ -0,0 +1,168 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cashier_reserve/common/base/ui.dart';
|
||||
import 'package:cashier_reserve/common/channel/model/call_status_change_model.dart';
|
||||
import 'package:cashier_reserve/common/manager/event_manager.dart';
|
||||
|
||||
class CallView extends StatefulWidget {
|
||||
final CallStatusChangeModel statusModel;
|
||||
final Function(String action)? onAction;
|
||||
|
||||
const CallView({super.key, required this.statusModel, this.onAction});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _CallViewState();
|
||||
}
|
||||
}
|
||||
|
||||
class _CallViewState extends State<CallView> {
|
||||
int _callDuration = 0;
|
||||
Timer? _callDurationTimer;
|
||||
|
||||
bool _isAccept = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
EventManager.addListener<CallReceivedEvent>(this, (event) {
|
||||
_acceptCall();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_cancelDurationTimer();
|
||||
EventManager.cancelListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.black.withOpacity(0.2),
|
||||
body: GestureDetector(
|
||||
onTap: () {
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
},
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
color: const Color(0x55000000),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.8,
|
||||
height: MediaQuery.of(context).size.height * 0.8,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.withOpacity(0.9),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: _buildCallInfo(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _getCallDuration() {
|
||||
// 将秒数转换为时分秒
|
||||
int hour = _callDuration ~/ 3600;
|
||||
int minute = _callDuration % 3600 ~/ 60;
|
||||
int second = _callDuration % 60;
|
||||
return "${hour.toString().padLeft(2, '0')}:${minute.toString().padLeft(2, '0')}:${second.toString().padLeft(2, '0')}";
|
||||
}
|
||||
|
||||
_acceptCall() {
|
||||
if (_isAccept) {
|
||||
return;
|
||||
}
|
||||
_isAccept = true;
|
||||
_startDurationTimer();
|
||||
}
|
||||
|
||||
_startDurationTimer() {
|
||||
_callDurationTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
setState(() {
|
||||
_callDuration++;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_cancelDurationTimer() {
|
||||
_callDurationTimer?.cancel();
|
||||
}
|
||||
|
||||
Widget _buildCallInfo(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_buildIncomingInfo(context),
|
||||
const SizedBox(height: 70),
|
||||
_buildActionBtn(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIncomingInfo(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"${widget.statusModel.number}",
|
||||
style: const TextStyle(
|
||||
fontSize: 35, color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"${widget.statusModel.region}",
|
||||
style: const TextStyle(fontSize: 15, color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(_getCallDuration(),
|
||||
style: const TextStyle(fontSize: 20, color: Colors.white)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionBtn(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
// 拒绝
|
||||
if (widget.onAction != null) {
|
||||
widget.onAction!("reject");
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Image.asset('images/call/reject.png', width: 60, height: 60),
|
||||
),
|
||||
),
|
||||
if (!_isAccept) const SizedBox(width: 80),
|
||||
if (!_isAccept) InkWell(
|
||||
onTap: () {
|
||||
// 接听
|
||||
if (widget.onAction != null) {
|
||||
widget.onAction!("accept");
|
||||
}
|
||||
|
||||
_acceptCall();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Image.asset('images/call/accept.png', width: 60, height: 60),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
11
lib/common/base/base_model.dart
Normal file
11
lib/common/base/base_model.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
import '../utils/func_tools.dart';
|
||||
|
||||
class BaseModel {
|
||||
/// 发生错误时错误信息, 有可能会被后面的请求覆盖掉
|
||||
String? msg;
|
||||
|
||||
String? getMsg() {
|
||||
return isEmptyString(msg) ? '' : msg;
|
||||
}
|
||||
}
|
||||
15
lib/common/base/provider.dart
Normal file
15
lib/common/base/provider.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:cashier_reserve/common/base/ui_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
export 'package:provider/provider.dart';
|
||||
|
||||
class MyProvider<T> {
|
||||
static T of<T>(BuildContext context, {bool listen = true}) {
|
||||
T provider = Provider.of<T>(context, listen: listen);
|
||||
if (provider is BaseUIModel) {
|
||||
BaseUIModel viewModel = provider;
|
||||
viewModel.context = context;
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
129
lib/common/base/ui.dart
Normal file
129
lib/common/base/ui.dart
Normal file
@@ -0,0 +1,129 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../push/push.dart';
|
||||
import './ui_model.dart';
|
||||
import './widgets.dart';
|
||||
|
||||
export 'package:flutter/material.dart';
|
||||
|
||||
class BaseUIController extends StatefulWidget {
|
||||
final BaseUI stateWidget;
|
||||
|
||||
const BaseUIController({super.key, required this.stateWidget});
|
||||
|
||||
@override
|
||||
State<BaseUIController> createState() {
|
||||
// ignore: no_logic_in_create_state
|
||||
return stateWidget;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BaseUI extends State<BaseUIController>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final provider = getProvider(context, listen: true);
|
||||
return Stack(
|
||||
children: [
|
||||
Scaffold(
|
||||
backgroundColor: getBackgroundColor(),
|
||||
appBar: getAppBar(context),
|
||||
body: _buildBody(context),
|
||||
bottomNavigationBar: getBottomNavigationBar(context),
|
||||
floatingActionButton: getFloatActionButton(context),
|
||||
),
|
||||
if (provider.showProgressHUD)
|
||||
Material(
|
||||
color: Colors.black.withAlpha(50),
|
||||
child: Center(
|
||||
child: Card(
|
||||
child: Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
color: const Color(0xaa000000),
|
||||
child: const Center(
|
||||
child: SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
dismissKeyboard(BuildContext context) {
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
}
|
||||
|
||||
Widget? getFloatActionButton(BuildContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget? getBottomNavigationBar(BuildContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@protected
|
||||
BaseUIModel getProvider(BuildContext context, {bool listen = true});
|
||||
|
||||
@protected
|
||||
String? getTitleStr(BuildContext context);
|
||||
|
||||
Future pushToPage<T extends BaseUIModel>(
|
||||
BuildContext context, BaseUIModel model) {
|
||||
return YJPush.pushWidget(
|
||||
context,
|
||||
getBuild<T>(model),
|
||||
);
|
||||
}
|
||||
|
||||
Widget getBuild<T extends BaseUIModel>(BaseUIModel model) {
|
||||
return ChangeNotifierProvider(
|
||||
create: (BuildContext context) {
|
||||
return model as T;
|
||||
},
|
||||
child: BaseUIController(
|
||||
stateWidget: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
AppBar? getAppBar(BuildContext context) {
|
||||
return makeAppbar(context, getTitleStr(context));
|
||||
}
|
||||
|
||||
Color getBackgroundColor() {
|
||||
return appBackGroundColor;
|
||||
}
|
||||
|
||||
bool hiddenBackBtn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Widget _buildBody(BuildContext context) {
|
||||
return GestureDetector(
|
||||
child: Container(
|
||||
color: getBackgroundColor(),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: buildBody(context),
|
||||
),
|
||||
onTap: () {
|
||||
dismissKeyboard(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
Widget buildBody(BuildContext context);
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => false;
|
||||
}
|
||||
46
lib/common/base/ui_model.dart
Normal file
46
lib/common/base/ui_model.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BaseUIModel with ChangeNotifier {
|
||||
static Function? staticNotifyListeners;
|
||||
BuildContext? context;
|
||||
bool showProgressHUD = false;
|
||||
|
||||
bool initialPage = true;
|
||||
|
||||
/// 判断页面是否被销毁
|
||||
bool disposed = false;
|
||||
|
||||
dismissKeyboard(BuildContext? context) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
}
|
||||
|
||||
showLoadingHud() {
|
||||
showProgressHUD = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
dismissLoadingHud() {
|
||||
showProgressHUD = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void notifyListeners() {
|
||||
if (!hasListeners) {
|
||||
return;
|
||||
}
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
super.notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposed = true;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
483
lib/common/base/widgets.dart
Normal file
483
lib/common/base/widgets.dart
Normal file
@@ -0,0 +1,483 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:cashier_reserve/common/base/ui.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
/// 应用主体背景颜色
|
||||
const Color appBackGroundColor = Color.fromARGB(255, 247, 250, 255);
|
||||
|
||||
/// 主色调
|
||||
const Color mdMainColor = Color(0xff2854BC);
|
||||
|
||||
/// fix for Flutter 2.5
|
||||
/// https://github.com/flutter/flutter/issues/77904
|
||||
Brightness? getAppBrightness(bool isLight) {
|
||||
if (kIsWeb) {
|
||||
return null;
|
||||
}
|
||||
if (Platform.isIOS) {
|
||||
return isLight ? Brightness.dark : Brightness.light;
|
||||
}
|
||||
return isLight ? Brightness.light : Brightness.dark;
|
||||
}
|
||||
|
||||
/// 微信风格Appbar
|
||||
AppBar makeWechatStyleAppbar(BuildContext context, String title,
|
||||
{List<Widget>? actions,
|
||||
final Color backGroundColor = Colors.white,
|
||||
final Color textColor = Colors.black,
|
||||
final Brightness brightness = Brightness.light,
|
||||
bool centerTitle = false,
|
||||
PreferredSizeWidget? bottom}) {
|
||||
return AppBar(
|
||||
backgroundColor: backGroundColor,
|
||||
elevation: 0,
|
||||
centerTitle: centerTitle,
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: getAppBrightness(false),
|
||||
statusBarBrightness: getAppBrightness(false)),
|
||||
actions: actions,
|
||||
bottom: bottom,
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 17, color: textColor, fontWeight: FontWeight.bold),
|
||||
),
|
||||
leading: IconButton(
|
||||
iconSize: 18,
|
||||
tooltip: "返回",
|
||||
color: Colors.black,
|
||||
icon: Icon(
|
||||
Icons.arrow_back_ios,
|
||||
color: textColor,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 通用Appbar
|
||||
AppBar makeAppbar(BuildContext context, String? title,
|
||||
{List<Widget>? actions,
|
||||
Color? backgroundColor,
|
||||
PreferredSizeWidget? bottom,
|
||||
final bool showBack = true,
|
||||
Widget? titleWidget,
|
||||
Widget? backWidget,
|
||||
VoidCallback? onBack,
|
||||
Key? key}) {
|
||||
backgroundColor ??= mdMainColor;
|
||||
return AppBar(
|
||||
key: key,
|
||||
backgroundColor: backgroundColor,
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
actions: actions,
|
||||
bottom: bottom,
|
||||
systemOverlayStyle: SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: getAppBrightness(false),
|
||||
statusBarBrightness: getAppBrightness(false)),
|
||||
title: titleWidget ??
|
||||
Text(
|
||||
title!,
|
||||
style: const TextStyle(
|
||||
fontSize: 17, color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
leading: showBack
|
||||
? (backWidget ??
|
||||
IconButton(
|
||||
iconSize: 18,
|
||||
tooltip: "返回",
|
||||
color: Colors.white,
|
||||
splashColor: Colors.black,
|
||||
hoverColor: Colors.black,
|
||||
icon: const Icon(Icons.arrow_back_ios),
|
||||
onPressed: onBack ??
|
||||
() {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
))
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// ListView 无感上拉加载
|
||||
class ListViewPullUpLoadMore {
|
||||
final ScrollController scrollController;
|
||||
final Future<void> Function() onLoadMore;
|
||||
|
||||
bool _isLoading = false;
|
||||
|
||||
ListViewPullUpLoadMore(this.scrollController, {required this.onLoadMore}) {
|
||||
scrollController.addListener(() {
|
||||
if (_isLoading) {
|
||||
return;
|
||||
}
|
||||
if (scrollController.offset ==
|
||||
scrollController.position.maxScrollExtent) {
|
||||
/// 到底部,添加加载中标记,触发回调
|
||||
_isLoading = true;
|
||||
onLoadMore().then((_) {
|
||||
/// 加载完毕,标记复位
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 绘制通用下划线
|
||||
Widget makeDivider(BuildContext? context,
|
||||
{Color? color, EdgeInsetsGeometry? padding, double? height}) {
|
||||
color ??= Colors.black.withAlpha(50);
|
||||
return Padding(
|
||||
padding: padding ??
|
||||
const EdgeInsets.only(left: 18.5, right: 18.5, top: 5, bottom: 5),
|
||||
child: Divider(
|
||||
color: color,
|
||||
height: height ?? 0.5,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 卡片容器
|
||||
Widget makeCardContainer(
|
||||
{required BuildContext context,
|
||||
required Widget child,
|
||||
Key? key,
|
||||
EdgeInsetsGeometry? padding,
|
||||
EdgeInsetsGeometry? paddingIn,
|
||||
BorderRadius? borderRadius,
|
||||
Color? color,
|
||||
double? elevation,
|
||||
double? width,
|
||||
bool nullWidth = false}) {
|
||||
return SizedBox(
|
||||
key: key,
|
||||
width: nullWidth ? null : (width ?? MediaQuery.of(context).size.width),
|
||||
child: Padding(
|
||||
padding: padding ?? const EdgeInsets.only(left: 8, right: 8),
|
||||
child: Card(
|
||||
color: color,
|
||||
elevation: elevation ?? 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: borderRadius ?? BorderRadius.circular(0),
|
||||
),
|
||||
child: Padding(
|
||||
padding:
|
||||
paddingIn ?? const EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: child,
|
||||
))));
|
||||
}
|
||||
|
||||
/// 通用页面加载动画
|
||||
Widget makeLoadBody(BuildContext context) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 通用网络图片
|
||||
Widget makeNetImage(BuildContext context, String? imageUrl,
|
||||
{BoxFit? fit, double? height, double? width}) {
|
||||
imageUrl ??= "https://image.xiaomfzs.com/xmf-app/user_icon.png";
|
||||
|
||||
if (kIsWeb) {
|
||||
return Image.network(imageUrl, fit: fit, height: height, width: width);
|
||||
}
|
||||
|
||||
return CachedNetworkImage(
|
||||
height: height,
|
||||
width: width,
|
||||
imageUrl: imageUrl,
|
||||
fit: fit,
|
||||
placeholder: (context, url) {
|
||||
return Container(
|
||||
color: Colors.black.withAlpha(50),
|
||||
child: Center(
|
||||
child: Image.asset("images/loading.png"),
|
||||
));
|
||||
},
|
||||
errorWidget: (BuildContext context, String url, dynamic error) {
|
||||
return const Icon(Icons.error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 通用头像
|
||||
Widget makeUserAvatar(BuildContext context, String? imgUrl,
|
||||
{final double size = 46}) {
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10000.0),
|
||||
child: makeNetImage(context, imgUrl, fit: BoxFit.fill),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 底部按钮边距容器
|
||||
Widget makeNavButtonPadding(Widget? widget) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 14),
|
||||
child: widget,
|
||||
);
|
||||
}
|
||||
|
||||
/// 底部按钮
|
||||
Widget makeBottomNavButton(String text,
|
||||
{required VoidCallback? onTap,
|
||||
Color color = mdMainColor,
|
||||
EdgeInsetsGeometry? padding,
|
||||
TextStyle? textStyle,
|
||||
double? elevation}) {
|
||||
return Padding(
|
||||
padding: padding ?? const EdgeInsets.only(left: 20, right: 20),
|
||||
child: ElevatedButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: color, elevation: elevation ?? 1),
|
||||
onPressed: onTap,
|
||||
child: Text(
|
||||
text,
|
||||
style: textStyle ??
|
||||
const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 收起键盘容器
|
||||
Widget makeDismissKeyboardContainer(BuildContext context, Widget child,
|
||||
{VoidCallback? onDismissed}) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
if (onDismissed != null) {
|
||||
onDismissed();
|
||||
}
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
/// 底部弹出按钮
|
||||
Future<String?> showBottomMenu(
|
||||
BuildContext context, Map<String, String> items) async {
|
||||
return await showCupertinoModalPopup<String?>(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
return CupertinoActionSheet(
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text(
|
||||
'取消',
|
||||
style: TextStyle(fontSize: 14, color: Colors.red),
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
for (var kv in (items.entries))
|
||||
CupertinoActionSheetAction(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, kv.key);
|
||||
},
|
||||
child: Text(
|
||||
kv.value,
|
||||
style: const TextStyle(fontSize: 14, color: Colors.black),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// 日期选择器
|
||||
Future<DateTime?> showMyDatePicker(
|
||||
BuildContext context, CupertinoDatePickerMode mode,
|
||||
{DateTime? defaultTime, DateTime? minTime, DateTime? maxTime}) async {
|
||||
final DateTime? date = await showModalBottomSheet<DateTime>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
DateTime pickedDate = DateTime.now();
|
||||
return SizedBox(
|
||||
height: 250,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
CupertinoButton(
|
||||
child: const Text('取消'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
CupertinoButton(
|
||||
child: const Text('确认'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context, pickedDate);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(
|
||||
height: 0,
|
||||
thickness: 1,
|
||||
),
|
||||
Expanded(
|
||||
child: CupertinoApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
locale: const Locale('zh', 'CN'),
|
||||
localizationsDelegates: const [
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [
|
||||
Locale('zh', 'CN'),
|
||||
Locale('en', 'US'),
|
||||
],
|
||||
home: CupertinoDatePicker(
|
||||
mode: mode,
|
||||
initialDateTime: defaultTime ?? DateTime.now(),
|
||||
minimumDate: minTime,
|
||||
maximumDate: maxTime,
|
||||
onDateTimeChanged: (DateTime dateTime) {
|
||||
pickedDate = dateTime;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
return date;
|
||||
}
|
||||
|
||||
/// appBar Search Widget
|
||||
PreferredSizeWidget makeAppBarSearchWidget(
|
||||
BuildContext context,
|
||||
String tipText,
|
||||
ValueChanged<String> onSubmitted, {
|
||||
double height = 56,
|
||||
double? width,
|
||||
TextInputType? textInputType,
|
||||
List<TextInputFormatter>? inputFormatters,
|
||||
ValueChanged<String>? onChanged,
|
||||
TextEditingController? controller,
|
||||
}) {
|
||||
return PreferredSize(
|
||||
child: SizedBox(
|
||||
height: height,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 5, left: 10, right: 10, bottom: 10),
|
||||
child: Container(
|
||||
width: width ?? MediaQuery.of(context).size.width,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withAlpha(100),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 15),
|
||||
child: const Icon(
|
||||
Icons.search,
|
||||
color: Colors.black45,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 5),
|
||||
width: MediaQuery.of(context).size.width -
|
||||
15 * 2 -
|
||||
15 -
|
||||
20 -
|
||||
5 -
|
||||
(width ?? 0),
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
textInputAction: TextInputAction.search,
|
||||
onSubmitted: onSubmitted,
|
||||
keyboardType: textInputType,
|
||||
inputFormatters: inputFormatters,
|
||||
onChanged: onChanged,
|
||||
decoration: InputDecoration(
|
||||
hintText: tipText,
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 15, color: Colors.black45),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
preferredSize: const Size(0, 42));
|
||||
}
|
||||
|
||||
Widget makeSearchWidget(
|
||||
BuildContext context, String hintText, ValueChanged<String> onSubmitted) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 5, left: 0, right: 0, bottom: 14),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color.fromARGB(255, 247, 250, 255),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 15),
|
||||
child: const Icon(
|
||||
Icons.search,
|
||||
color: Colors.black45,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 5),
|
||||
width: MediaQuery.of(context).size.width - 15 * 2 - 82,
|
||||
child: TextField(
|
||||
textInputAction: TextInputAction.search,
|
||||
onSubmitted: onSubmitted,
|
||||
onChanged: (str) {
|
||||
if (str == "") {
|
||||
onSubmitted("");
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
hintStyle:
|
||||
const TextStyle(fontSize: 15, color: Colors.black45),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
class AlwaysDisabledFocusNode extends FocusNode {
|
||||
@override
|
||||
bool get hasFocus => false;
|
||||
}
|
||||
54
lib/common/channel/channel_event.dart
Normal file
54
lib/common/channel/channel_event.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cashier_reserve/common/channel/model/call_status_change_model.dart';
|
||||
import 'package:cashier_reserve/common/print/print.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import '../manager/event_manager.dart';
|
||||
import 'model/call_log_model.dart';
|
||||
import 'names.dart';
|
||||
|
||||
class MyEventChannel {
|
||||
static void startListener() {
|
||||
onGetCallLogResult();
|
||||
onCallStatusChange();
|
||||
}
|
||||
|
||||
static void onGetCallLogResult() {
|
||||
EventChannel channel = EventChannel(getChannelName(kCallLogCallback));
|
||||
channel.receiveBroadcastStream().listen((Object? o) {
|
||||
GetCallLogEvent event = GetCallLogEvent();
|
||||
if (o is String) {
|
||||
event.isSuccess = true;
|
||||
List<dynamic> list = json.decode(o);
|
||||
List<CallLogModel> callLogs = [];
|
||||
for (var item in list) {
|
||||
callLogs.add(CallLogModel.fromJson(item));
|
||||
if (callLogs.length >= 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
event.callLogs = callLogs.reversed.toList();
|
||||
} else {
|
||||
event.isSuccess = false;
|
||||
}
|
||||
EventManager.postEvent(event);
|
||||
}, onError: (Object error) {
|
||||
yjPrint("onGetCallLogResult error");
|
||||
});
|
||||
}
|
||||
|
||||
static void onCallStatusChange() {
|
||||
EventChannel channel = EventChannel(getChannelName(kCallStatusChange));
|
||||
channel.receiveBroadcastStream().listen((Object? o) {
|
||||
yjPrint("onCallStatusChange: $o");
|
||||
if (o is String) {
|
||||
Map<String, dynamic> m = json.decode(o);
|
||||
CallStatusChangeModel model = CallStatusChangeModel.fromJson(m);
|
||||
EventManager.postEvent(CallStatusChangeEvent(model: model));
|
||||
}
|
||||
}, onError: (Object error) {
|
||||
yjPrint("onCallStatusChange error");
|
||||
});
|
||||
}
|
||||
}
|
||||
46
lib/common/channel/channel_manager.dart
Normal file
46
lib/common/channel/channel_manager.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import '../print/print.dart';
|
||||
import 'names.dart';
|
||||
|
||||
class ChannelManager {
|
||||
static Future<void> getCallLog(String param) async {
|
||||
MethodChannel channel = MethodChannel(getChannelName(kGetCallLog));
|
||||
try {
|
||||
final result = await channel.invokeMethod(kGetCallLog, param);
|
||||
yjPrint(result);
|
||||
} on PlatformException catch (e) {
|
||||
yjPrint('$kGetCallLog 发生异常:$e');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> acceptCall() async {
|
||||
MethodChannel channel = MethodChannel(getChannelName(kAcceptCall));
|
||||
try {
|
||||
final result = await channel.invokeMethod(kAcceptCall);
|
||||
yjPrint(result);
|
||||
} on PlatformException catch (e) {
|
||||
yjPrint('$kAcceptCall 发生异常:$e');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> rejectCall() async {
|
||||
MethodChannel channel = MethodChannel(getChannelName(kRejectCall));
|
||||
try {
|
||||
final result = await channel.invokeMethod(kRejectCall);
|
||||
yjPrint(result);
|
||||
} on PlatformException catch (e) {
|
||||
yjPrint('$kRejectCall 发生异常:$e');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> endCall() async {
|
||||
MethodChannel channel = MethodChannel(getChannelName(kEndCall));
|
||||
try {
|
||||
final result = await channel.invokeMethod(kEndCall);
|
||||
yjPrint(result);
|
||||
} on PlatformException catch (e) {
|
||||
yjPrint('$kEndCall 发生异常:$e');
|
||||
}
|
||||
}
|
||||
}
|
||||
86
lib/common/channel/model/call_log_model.dart
Normal file
86
lib/common/channel/model/call_log_model.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// number : "18092171236"
|
||||
/// date : "2024-11-21 09:32:27"
|
||||
/// duration : 0
|
||||
/// type : 5
|
||||
/// name : gong
|
||||
|
||||
CallLogModel callLogModelFromJson(String str) =>
|
||||
CallLogModel.fromJson(json.decode(str));
|
||||
|
||||
String callLogModelToJson(CallLogModel data) => json.encode(data.toJson());
|
||||
|
||||
class CallLogModel {
|
||||
CallLogModel({
|
||||
String? number,
|
||||
String? date,
|
||||
num? duration,
|
||||
num? type,
|
||||
String? name,
|
||||
String? time,
|
||||
}) {
|
||||
_number = number;
|
||||
_date = date;
|
||||
_duration = duration;
|
||||
_type = type;
|
||||
_name = name;
|
||||
_time = time;
|
||||
}
|
||||
|
||||
CallLogModel.fromJson(dynamic json) {
|
||||
_number = json['number'];
|
||||
_date = json['date'];
|
||||
_duration = json['duration'];
|
||||
_type = json['type'];
|
||||
_name = json['name'];
|
||||
_time = json['time'];
|
||||
}
|
||||
|
||||
String? _number;
|
||||
String? _date;
|
||||
num? _duration;
|
||||
num? _type;
|
||||
String? _name;
|
||||
String? _time;
|
||||
|
||||
CallLogModel copyWith({
|
||||
String? number,
|
||||
String? date,
|
||||
num? duration,
|
||||
num? type,
|
||||
String? name,
|
||||
String? time,
|
||||
}) =>
|
||||
CallLogModel(
|
||||
number: number ?? _number,
|
||||
date: date ?? _date,
|
||||
duration: duration ?? _duration,
|
||||
type: type ?? _type,
|
||||
name: name ?? _name,
|
||||
time: time ?? _time,
|
||||
);
|
||||
|
||||
String? get number => _number;
|
||||
|
||||
String? get date => _date;
|
||||
|
||||
num? get duration => _duration;
|
||||
|
||||
num? get type => _type;
|
||||
|
||||
String? get name => _name;
|
||||
|
||||
String? get time => _time;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['number'] = _number;
|
||||
map['date'] = _date;
|
||||
map['duration'] = _duration;
|
||||
map['type'] = _type;
|
||||
map['name'] = _name;
|
||||
map['time'] = _time;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
54
lib/common/channel/model/call_status_change_model.dart
Normal file
54
lib/common/channel/model/call_status_change_model.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
import 'dart:convert';
|
||||
/// state : "Incoming"
|
||||
/// number : "18020143310"
|
||||
/// name : "gong"
|
||||
/// region : "江苏省 南京市"
|
||||
|
||||
CallStatusChangeModel callStatusChangeModelFromJson(String str) => CallStatusChangeModel.fromJson(json.decode(str));
|
||||
String callStatusChangeModelToJson(CallStatusChangeModel data) => json.encode(data.toJson());
|
||||
class CallStatusChangeModel {
|
||||
CallStatusChangeModel({
|
||||
String? state,
|
||||
String? number,
|
||||
String? name,
|
||||
String? region,}){
|
||||
_state = state;
|
||||
_number = number;
|
||||
_name = name;
|
||||
_region = region;
|
||||
}
|
||||
|
||||
CallStatusChangeModel.fromJson(dynamic json) {
|
||||
_state = json['state'];
|
||||
_number = json['number'];
|
||||
_name = json['name'];
|
||||
_region = json['region'];
|
||||
}
|
||||
String? _state;
|
||||
String? _number;
|
||||
String? _name;
|
||||
String? _region;
|
||||
CallStatusChangeModel copyWith({ String? state,
|
||||
String? number,
|
||||
String? name,
|
||||
String? region,
|
||||
}) => CallStatusChangeModel( state: state ?? _state,
|
||||
number: number ?? _number,
|
||||
name: name ?? _name,
|
||||
region: region ?? _region,
|
||||
);
|
||||
String? get state => _state;
|
||||
String? get number => _number;
|
||||
String? get name => _name;
|
||||
String? get region => _region;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['state'] = _state;
|
||||
map['number'] = _number;
|
||||
map['name'] = _name;
|
||||
map['region'] = _region;
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
14
lib/common/channel/names.dart
Normal file
14
lib/common/channel/names.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
const String kChannelBaseName = 'com.czg.cashier_reserve/';
|
||||
|
||||
const String kGetCallLog = 'getCallLog';
|
||||
|
||||
const String kCallLogCallback = 'callLogCallback';
|
||||
|
||||
const String kCallStatusChange = 'callStatusChange';
|
||||
const String kAcceptCall = 'acceptCall';
|
||||
const String kEndCall = 'endCall';
|
||||
const String kRejectCall = 'rejectCall';
|
||||
|
||||
String getChannelName(name) {
|
||||
return kChannelBaseName + name;
|
||||
}
|
||||
45
lib/common/encrypt/pwd.dart
Normal file
45
lib/common/encrypt/pwd.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
import 'package:cashier_reserve/common/print/print.dart';
|
||||
import 'package:encrypt/encrypt.dart';
|
||||
import 'package:pointycastle/asymmetric/api.dart';
|
||||
|
||||
class PwdEncrypt {
|
||||
static const _rsaPubKey =
|
||||
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD2iRe41HdTNF8RUhNnHit5NpMNtGL0NPTSSpPjjI1kJfVorRvaQerUgkCAwEAAQ==";
|
||||
|
||||
static String encrypt(String pwd) {
|
||||
return _encryptString(publicKeyString: _rsaPubKey, plainText: pwd);
|
||||
}
|
||||
|
||||
/// 利用公钥进行加密
|
||||
static String _encryptString(
|
||||
{required String publicKeyString, required String plainText}) {
|
||||
String key = '-----BEGIN PUBLIC KEY-----\n\r';
|
||||
int length = 0;
|
||||
const baseLen = 64;
|
||||
while (length < publicKeyString.length) {
|
||||
bool over = length + baseLen > publicKeyString.length;
|
||||
key += publicKeyString.substring(
|
||||
length, over ? publicKeyString.length : length + baseLen);
|
||||
key += '\n\r';
|
||||
length += baseLen;
|
||||
}
|
||||
key += '-----END PUBLIC KEY-----';
|
||||
|
||||
yjPrint(key);
|
||||
|
||||
final publicKey = _parsePublicKeyFromPem(key);
|
||||
|
||||
final encrypt = Encrypter(RSA(publicKey: publicKey));
|
||||
|
||||
final encryptedText = encrypt.encrypt(plainText);
|
||||
|
||||
return encryptedText.base64;
|
||||
}
|
||||
|
||||
/// 通过PEM字符串解析公钥字符串
|
||||
static RSAPublicKey _parsePublicKeyFromPem(String pemString) {
|
||||
final key = RSAKeyParser().parse(pemString);
|
||||
return key as RSAPublicKey;
|
||||
}
|
||||
}
|
||||
182
lib/common/local/cupertino_localizations_delegate.dart
Normal file
182
lib/common/local/cupertino_localizations_delegate.dart
Normal file
@@ -0,0 +1,182 @@
|
||||
import 'package:flutter/foundation.dart' show SynchronousFuture;
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class CupertinoLocalizationsDelegate
|
||||
extends LocalizationsDelegate<CupertinoLocalizations> {
|
||||
const CupertinoLocalizationsDelegate();
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) =>
|
||||
<String>['en', 'zh'].contains(locale.languageCode);
|
||||
|
||||
@override
|
||||
SynchronousFuture<_DefaultCupertinoLocalizations> load(Locale locale) {
|
||||
return SynchronousFuture<_DefaultCupertinoLocalizations>(
|
||||
_DefaultCupertinoLocalizations(locale.languageCode));
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReload(CupertinoLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
||||
class _DefaultCupertinoLocalizations extends CupertinoLocalizations {
|
||||
_DefaultCupertinoLocalizations(this._languageCode);
|
||||
|
||||
final DefaultCupertinoLocalizations _en =
|
||||
const DefaultCupertinoLocalizations();
|
||||
final String _languageCode;
|
||||
|
||||
final Map<String, Map<String, String>> _dict = <String, Map<String, String>>{
|
||||
'en': <String, String>{
|
||||
'alert': 'Alert',
|
||||
'copy': 'Copy',
|
||||
'paste': 'Paste',
|
||||
'cut': 'Cut',
|
||||
'selectAll': 'Select all',
|
||||
'today': 'today'
|
||||
},
|
||||
'zh': <String, String>{
|
||||
'alert': '警告',
|
||||
'copy': '复制',
|
||||
'paste': '粘贴',
|
||||
'cut': '剪切',
|
||||
'selectAll': '选择全部',
|
||||
'today': '今天'
|
||||
}
|
||||
};
|
||||
|
||||
@override
|
||||
String get alertDialogLabel => _get('alert')!;
|
||||
|
||||
@override
|
||||
String get anteMeridiemAbbreviation => _en.anteMeridiemAbbreviation;
|
||||
|
||||
@override
|
||||
String get postMeridiemAbbreviation => _en.postMeridiemAbbreviation;
|
||||
|
||||
@override
|
||||
String get copyButtonLabel => _get('copy')!;
|
||||
|
||||
@override
|
||||
String get cutButtonLabel => _get('cut')!;
|
||||
|
||||
@override
|
||||
String get pasteButtonLabel => _get('paste')!;
|
||||
|
||||
@override
|
||||
String get selectAllButtonLabel => _get('selectAll')!;
|
||||
|
||||
@override
|
||||
DatePickerDateOrder get datePickerDateOrder => _en.datePickerDateOrder;
|
||||
|
||||
@override
|
||||
DatePickerDateTimeOrder get datePickerDateTimeOrder =>
|
||||
_en.datePickerDateTimeOrder;
|
||||
|
||||
|
||||
|
||||
@override
|
||||
String datePickerHour(int hour) => _en.datePickerHour(hour);
|
||||
|
||||
@override
|
||||
String datePickerHourSemanticsLabel(int hour) =>
|
||||
_en.datePickerHourSemanticsLabel(hour);
|
||||
|
||||
@override
|
||||
String datePickerMediumDate(DateTime date) => _en.datePickerMediumDate(date);
|
||||
|
||||
@override
|
||||
String datePickerMinute(int minute) => _en.datePickerMinute(minute);
|
||||
|
||||
@override
|
||||
String datePickerMinuteSemanticsLabel(int minute) =>
|
||||
_en.datePickerMinuteSemanticsLabel(minute);
|
||||
|
||||
@override
|
||||
String datePickerMonth(int monthIndex) => _en.datePickerMonth(monthIndex);
|
||||
|
||||
@override
|
||||
String datePickerYear(int yearIndex) => _en.datePickerYear(yearIndex);
|
||||
|
||||
@override
|
||||
String timerPickerHour(int hour) => _en.timerPickerHour(hour);
|
||||
|
||||
@override
|
||||
String timerPickerHourLabel(int hour) => _en.timerPickerHourLabel(hour);
|
||||
|
||||
@override
|
||||
String timerPickerMinute(int minute) => _en.timerPickerMinute(minute);
|
||||
|
||||
@override
|
||||
String timerPickerMinuteLabel(int minute) =>
|
||||
_en.timerPickerMinuteLabel(minute);
|
||||
|
||||
@override
|
||||
String timerPickerSecond(int second) => _en.timerPickerSecond(second);
|
||||
|
||||
@override
|
||||
String timerPickerSecondLabel(int second) =>
|
||||
_en.timerPickerSecondLabel(second);
|
||||
|
||||
String? _get(String key) {
|
||||
return _dict[_languageCode]![key];
|
||||
}
|
||||
|
||||
@override
|
||||
String get todayLabel => _get("today")!;
|
||||
|
||||
@override
|
||||
String get modalBarrierDismissLabel => _en.modalBarrierDismissLabel;
|
||||
|
||||
@override
|
||||
String tabSemanticsLabel({required int tabIndex, required int tabCount}) {
|
||||
return _en.tabSemanticsLabel(tabIndex: tabIndex, tabCount: tabCount);
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> get timerPickerHourLabels => _en.timerPickerHourLabels;
|
||||
|
||||
@override
|
||||
List<String> get timerPickerMinuteLabels => _en.timerPickerMinuteLabels;
|
||||
|
||||
@override
|
||||
List<String> get timerPickerSecondLabels => _en.timerPickerSecondLabels;
|
||||
|
||||
@override
|
||||
String get searchTextFieldPlaceholderLabel => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
String datePickerDayOfMonth(int dayIndex, [int? weekDay]) {
|
||||
return _en.datePickerDayOfMonth(dayIndex, weekDay);
|
||||
}
|
||||
|
||||
@override
|
||||
// TODO: implement noSpellCheckReplacementsLabel
|
||||
String get noSpellCheckReplacementsLabel => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
String datePickerStandaloneMonth(int monthIndex) {
|
||||
// TODO: implement datePickerStandaloneMonth
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
// TODO: implement lookUpButtonLabel
|
||||
String get lookUpButtonLabel => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
// TODO: implement menuDismissLabel
|
||||
String get menuDismissLabel => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
// TODO: implement searchWebButtonLabel
|
||||
String get searchWebButtonLabel => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
// TODO: implement shareButtonLabel
|
||||
String get shareButtonLabel => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
// TODO: implement clearButtonLabel
|
||||
String get clearButtonLabel => throw UnimplementedError();
|
||||
}
|
||||
52
lib/common/local/timeago.dart
Normal file
52
lib/common/local/timeago.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
import 'package:timeago/timeago.dart';
|
||||
|
||||
/// Chinese-China messages
|
||||
class ZhCnMessages implements LookupMessages {
|
||||
@override
|
||||
String prefixAgo() => '';
|
||||
|
||||
@override
|
||||
String prefixFromNow() => '';
|
||||
|
||||
@override
|
||||
String suffixAgo() => '前';
|
||||
|
||||
@override
|
||||
String suffixFromNow() => '后';
|
||||
|
||||
@override
|
||||
String lessThanOneMinute(int seconds) => '不到一分钟';
|
||||
|
||||
@override
|
||||
String aboutAMinute(int minutes) => '约 1 分钟';
|
||||
|
||||
@override
|
||||
String minutes(int minutes) => '$minutes 分钟';
|
||||
|
||||
@override
|
||||
String aboutAnHour(int minutes) => '约 1 小时';
|
||||
|
||||
@override
|
||||
String hours(int hours) => '约 $hours 小时';
|
||||
|
||||
@override
|
||||
String aDay(int hours) => '约 1 天';
|
||||
|
||||
@override
|
||||
String days(int days) => '约 $days 日';
|
||||
|
||||
@override
|
||||
String aboutAMonth(int days) => '约 1 个月';
|
||||
|
||||
@override
|
||||
String months(int months) => '约 $months 月';
|
||||
|
||||
@override
|
||||
String aboutAYear(int year) => '约 1 年';
|
||||
|
||||
@override
|
||||
String years(int years) => '约 $years 年';
|
||||
|
||||
@override
|
||||
String wordSeparator() => '';
|
||||
}
|
||||
129
lib/common/manager/app_manager.dart
Normal file
129
lib/common/manager/app_manager.dart
Normal file
@@ -0,0 +1,129 @@
|
||||
import 'package:cashier_reserve/common/print/print.dart';
|
||||
import 'package:cashier_reserve/common/push/push.dart';
|
||||
import 'package:cashier_reserve/data_model/login/login_result.dart';
|
||||
import 'package:cashier_reserve/login/login_view.dart';
|
||||
import 'package:cashier_reserve/model/reserve_model.dart';
|
||||
import 'package:cashier_reserve/model/version_model.dart';
|
||||
import 'package:cashier_reserve/update_version/update_version_view.dart';
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
import '../base/ui.dart';
|
||||
import '../channel/channel_event.dart';
|
||||
import '../utils/func_tools.dart';
|
||||
import 'hive_manager.dart';
|
||||
|
||||
class AppManager {
|
||||
static BuildContext? globalContext;
|
||||
static bool _isAlertLogin = false;
|
||||
|
||||
static String _smsContent = "";
|
||||
|
||||
static Future<void> initThirdPackage() async {
|
||||
MyEventChannel.startListener();
|
||||
|
||||
await HiveManager.initHive();
|
||||
|
||||
EasyRefresh.defaultHeaderBuilder = () => const ClassicHeader(
|
||||
dragText: "下拉刷新",
|
||||
readyText: "释放刷新",
|
||||
armedText: "正在刷新",
|
||||
processingText: "正在刷新",
|
||||
processedText: "刷新完成",
|
||||
noMoreText: "没有更多数据了",
|
||||
failedText: "刷新失败",
|
||||
messageText: "上次刷新时间:%T",
|
||||
showText: true,
|
||||
showMessage: true,
|
||||
iconDimension: 30,
|
||||
spacing: 10,
|
||||
);
|
||||
}
|
||||
|
||||
static void setGlobalContext(BuildContext context) {
|
||||
globalContext = context;
|
||||
}
|
||||
|
||||
static bool isLogin() {
|
||||
String token = HiveManager.getUserToken();
|
||||
if (isEmptyString(token)) {
|
||||
gotoLogin();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gotoLogin() {
|
||||
if (_isAlertLogin) {
|
||||
return;
|
||||
}
|
||||
_isAlertLogin = true;
|
||||
|
||||
while (Navigator.of(globalContext!).canPop()) {
|
||||
Navigator.of(globalContext!).pop();
|
||||
}
|
||||
|
||||
YJPush.presentWidget(globalContext!, const LoginView());
|
||||
}
|
||||
|
||||
static void checkAppVersion() async {
|
||||
final res = await VersionModel.requestNewVersionInfo();
|
||||
yjPrint(res);
|
||||
|
||||
if (res == null || res.version == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
yjPrint("version == ${packageInfo.version}");
|
||||
|
||||
List<String> serverList = res.version!.split(".");
|
||||
List<String> localList = packageInfo.version.split(".");
|
||||
|
||||
if (serverList.length != 3 || localList.length != 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
int serverVersion = int.parse(serverList[0]) * 10000 + int.parse(serverList[1]) * 100 + int.parse(serverList[2]);
|
||||
int localVersion = int.parse(localList[0]) * 10000 + int.parse(localList[1]) * 100 + int.parse(localList[2]);
|
||||
|
||||
if (serverVersion <= localVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
yjPrint("serverVersion == $serverVersion, localVersion == $localVersion");
|
||||
|
||||
YJPush.presentWidget(globalContext!, UpdateVersionView(versionModel: res,));
|
||||
}
|
||||
|
||||
static void disposeLoginWidget() {
|
||||
_isAlertLogin = false;
|
||||
}
|
||||
|
||||
static bool isShowLoginView() {
|
||||
return _isAlertLogin;
|
||||
}
|
||||
|
||||
static String getUserToken() {
|
||||
return HiveManager.getUserToken();
|
||||
}
|
||||
|
||||
static void loginSuccess(LoginResult? r) {
|
||||
HiveManager.setUserToken(r?.token ?? '');
|
||||
HiveManager.setShopId((r?.shopId ?? '').toString());
|
||||
HiveManager.setShopName(r?.shopName ?? '');
|
||||
HiveManager.setShopLogo(r?.logo ?? '');
|
||||
HiveManager.setUserInfo(r?.user?.toString() ?? '');
|
||||
|
||||
disposeLoginWidget();
|
||||
Navigator.of(globalContext!).pop();
|
||||
}
|
||||
|
||||
static Future<String> loadReserveSms() async {
|
||||
if (_smsContent.isNotEmpty) {
|
||||
return _smsContent;
|
||||
}
|
||||
_smsContent = await ReserveModel.getReserveSms();
|
||||
return _smsContent;
|
||||
}
|
||||
}
|
||||
69
lib/common/manager/event_manager.dart
Normal file
69
lib/common/manager/event_manager.dart
Normal file
@@ -0,0 +1,69 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cashier_reserve/common/channel/model/call_log_model.dart';
|
||||
import 'package:cashier_reserve/common/channel/model/call_status_change_model.dart';
|
||||
import 'package:event_bus/event_bus.dart';
|
||||
|
||||
|
||||
class EventManager {
|
||||
static EventBus? _eventBus;
|
||||
|
||||
static final Map<dynamic, List<StreamSubscription>> _eventMap = {};
|
||||
|
||||
static EventBus? get eventBus => getEventBus();
|
||||
|
||||
static EventBus? getEventBus() {
|
||||
_eventBus ??= EventBus();
|
||||
return _eventBus;
|
||||
}
|
||||
|
||||
static void postEvent(MyEvent event) {
|
||||
getEventBus()!.fire(event);
|
||||
}
|
||||
|
||||
static void addListener<T>(dynamic widget, void Function(T event) onData) {
|
||||
StreamSubscription event = EventManager.eventBus!.on<T>().listen((T e) {
|
||||
onData(e);
|
||||
});
|
||||
List<StreamSubscription>? list = _eventMap[widget];
|
||||
list ??= [];
|
||||
list.add(event);
|
||||
_eventMap[widget] = list;
|
||||
}
|
||||
|
||||
static void cancelListener(dynamic widget) {
|
||||
List<StreamSubscription>? list = _eventMap[widget];
|
||||
if (list == null) {
|
||||
return;
|
||||
}
|
||||
for (var event in list) {
|
||||
event.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MyEvent {
|
||||
String name = '';
|
||||
}
|
||||
|
||||
class GetCallLogEvent extends MyEvent {
|
||||
List<CallLogModel> callLogs;
|
||||
bool isLoadMore = false;
|
||||
bool isSuccess = false;
|
||||
|
||||
GetCallLogEvent({this.callLogs = const [], this.isLoadMore = false, this.isSuccess = false});
|
||||
}
|
||||
|
||||
class CallStatusChangeEvent extends MyEvent {
|
||||
/// state = IncomingNumberReceived 来电接听
|
||||
/// state = OutGoing 呼出
|
||||
/// state = End 通话结束
|
||||
/// state = Incoming 来电
|
||||
CallStatusChangeModel model;
|
||||
|
||||
CallStatusChangeEvent({required this.model});
|
||||
}
|
||||
|
||||
class CallReceivedEvent extends MyEvent {
|
||||
CallReceivedEvent();
|
||||
}
|
||||
61
lib/common/manager/hive_manager.dart
Normal file
61
lib/common/manager/hive_manager.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class HiveManager {
|
||||
static Box? _userInfoBox;
|
||||
|
||||
static Future<void> initHive() async {
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
Hive.defaultDirectory = dir.path;
|
||||
|
||||
_userInfoBox = Hive.box();
|
||||
}
|
||||
|
||||
static void clearLoginInfo() {
|
||||
_userInfoBox?.delete('token');
|
||||
_userInfoBox?.delete('shopId');
|
||||
_userInfoBox?.delete('shopName');
|
||||
_userInfoBox?.delete('shopLogo');
|
||||
_userInfoBox?.delete('userInfo');
|
||||
}
|
||||
|
||||
static void setUserToken(String token) {
|
||||
_userInfoBox?.put('token', token);
|
||||
}
|
||||
|
||||
static String getUserToken() {
|
||||
return _userInfoBox?.get('token') ?? '';
|
||||
}
|
||||
|
||||
static void setShopId(String shopId) {
|
||||
_userInfoBox?.put('shopId', shopId);
|
||||
}
|
||||
|
||||
static String getShopId() {
|
||||
return _userInfoBox?.get('shopId') ?? '';
|
||||
}
|
||||
|
||||
static void setShopName(String shopName) {
|
||||
_userInfoBox?.put('shopName', shopName);
|
||||
}
|
||||
|
||||
static String getShopName() {
|
||||
return _userInfoBox?.get('shopName') ?? '';
|
||||
}
|
||||
|
||||
static void setShopLogo(String shopLogo) {
|
||||
_userInfoBox?.put('shopLogo', shopLogo);
|
||||
}
|
||||
|
||||
static String getShopLogo() {
|
||||
return _userInfoBox?.get('shopLogo') ?? '';
|
||||
}
|
||||
|
||||
static void setUserInfo(String userInfo) {
|
||||
_userInfoBox?.put('userInfo', userInfo);
|
||||
}
|
||||
|
||||
static String getUserInfo() {
|
||||
return _userInfoBox?.get('userInfo') ?? '';
|
||||
}
|
||||
}
|
||||
10
lib/common/print/print.dart
Normal file
10
lib/common/print/print.dart
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
const bool inProduction = !kDebugMode;
|
||||
|
||||
void yjPrint(Object? object) {
|
||||
if (kDebugMode) {
|
||||
print(object);
|
||||
}
|
||||
}
|
||||
87
lib/common/push/push.dart
Normal file
87
lib/common/push/push.dart
Normal file
@@ -0,0 +1,87 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class YJPush {
|
||||
static Future presentWidget(BuildContext context, Widget widget) {
|
||||
return Navigator.of(context).push(PageRouteBuilder(
|
||||
pageBuilder: (BuildContext context, Animation<double> animation,
|
||||
Animation<double> secondaryAnimation) {
|
||||
return widget;
|
||||
},
|
||||
transitionsBuilder: (
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
Widget child,
|
||||
) {
|
||||
// 添加一个平移动画
|
||||
return YJPush.createTransition(animation, child);
|
||||
},
|
||||
opaque: false,
|
||||
barrierDismissible: true));
|
||||
}
|
||||
|
||||
static void presentWidgetNoAnimation(BuildContext context, Widget widget) {
|
||||
Navigator.of(context).push(PageRouteBuilder(
|
||||
pageBuilder: (BuildContext context, Animation<double> animation,
|
||||
Animation<double> secondaryAnimation) {
|
||||
return widget;
|
||||
},
|
||||
transitionsBuilder: (
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
Widget child,
|
||||
) {
|
||||
// 添加一个平移动画
|
||||
return YJPush.createNoAnimationTransition(animation, child);
|
||||
},
|
||||
opaque: false,
|
||||
barrierDismissible: true));
|
||||
}
|
||||
|
||||
/// 创建一个平移变换
|
||||
/// 跳转过去查看源代码,可以看到有各种各样定义好的变换
|
||||
static SlideTransition createTransition(
|
||||
Animation<double> animation, Widget child) {
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(0.0, 1.0),
|
||||
end: const Offset(0.0, 0.0),
|
||||
).animate(animation),
|
||||
child: child, // child is the value returned by pageBuilder
|
||||
);
|
||||
}
|
||||
|
||||
/// 创建一个平移变换 没有动画
|
||||
static SlideTransition createNoAnimationTransition(
|
||||
Animation<double> animation, Widget child) {
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(0.0, 0.0),
|
||||
end: const Offset(0.0, 0.0),
|
||||
).animate(animation),
|
||||
child: child, // child is the value returned by pageBuilder
|
||||
);
|
||||
}
|
||||
|
||||
static Future pushWidget(BuildContext context, Widget widget) async {
|
||||
Route route = CupertinoPageRoute(builder: (context) => widget);
|
||||
return await Navigator.push(context, route);
|
||||
}
|
||||
|
||||
static Future pushAndRemoveWidget(BuildContext context, Widget widget) async {
|
||||
Route route = CupertinoPageRoute(builder: (context) => widget);
|
||||
return await Navigator.pushAndRemoveUntil(context, route, (route) => false);
|
||||
}
|
||||
|
||||
static SlideTransition createHTransition(
|
||||
Animation<double> animation, Widget child) {
|
||||
return SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(1.0, 0.0),
|
||||
end: const Offset(0.0, 0.0),
|
||||
).animate(animation),
|
||||
child: child, // child is the value returned by pageBuilder
|
||||
);
|
||||
}
|
||||
}
|
||||
115
lib/common/request/request_manager.dart
Normal file
115
lib/common/request/request_manager.dart
Normal file
@@ -0,0 +1,115 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cashier_reserve/common/manager/app_manager.dart';
|
||||
import 'package:cashier_reserve/common/manager/hive_manager.dart';
|
||||
import 'package:cashier_reserve/common/print/print.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../utils/utils.dart';
|
||||
|
||||
const String kBaseUrl = 'http://192.168.1.31';
|
||||
// const String kBaseUrl = 'https://cashier.sxczgkj.com';
|
||||
|
||||
const kSuccessCode = 200;
|
||||
const kNeedLoginCode = 401;
|
||||
|
||||
const Map<String, dynamic> _emptyMap = {};
|
||||
|
||||
class RequestManager {
|
||||
/// HttpClient
|
||||
static final Dio _c = Dio(BaseOptions(
|
||||
baseUrl: kBaseUrl,
|
||||
connectTimeout: const Duration(milliseconds: 5000),
|
||||
receiveTimeout: const Duration(milliseconds: 5000)));
|
||||
|
||||
/// GET
|
||||
static Future<dynamic> get(String url,
|
||||
{Map<String, dynamic> params = _emptyMap, bool catchError = true}) {
|
||||
if (url.contains("?")) {
|
||||
url += "&shopId=${HiveManager.getShopId()}";
|
||||
} else {
|
||||
url += "?shopId=${HiveManager.getShopId()}";
|
||||
}
|
||||
|
||||
// 处理传入的其他参数
|
||||
if (params.isNotEmpty) {
|
||||
String paramString = Uri(queryParameters: params).query;
|
||||
url += "&$paramString";
|
||||
}
|
||||
|
||||
return _doRequest("GET", url, catchError: catchError);
|
||||
}
|
||||
|
||||
/// DELETE
|
||||
static Future<dynamic> delete(String url, {bool catchError = true}) {
|
||||
return _doRequest("DELETE", url, catchError: catchError);
|
||||
}
|
||||
|
||||
/// POST
|
||||
static Future<dynamic> post(String url, Map<String, dynamic>? body,
|
||||
{bool catchError = true}) {
|
||||
if (body != null) {
|
||||
body["shopId"] = HiveManager.getShopId();
|
||||
} else {
|
||||
body = {"shopId": HiveManager.getShopId()};
|
||||
}
|
||||
return _doRequest("POST", url, body: body, catchError: catchError);
|
||||
}
|
||||
|
||||
/// PUT
|
||||
static Future<dynamic> put(String url, Map<String, dynamic>? body,
|
||||
{bool catchError = true}) {
|
||||
if (body != null) {
|
||||
body["shopId"] = HiveManager.getShopId();
|
||||
} else {
|
||||
body = {"shopId": HiveManager.getShopId()};
|
||||
}
|
||||
return _doRequest("PUT", url, body: body, catchError: catchError);
|
||||
}
|
||||
|
||||
static Future<dynamic> _doRequest(String method, String url,
|
||||
{Map<String, dynamic>? body, required bool catchError}) async {
|
||||
yjPrint("[RequestManager req]: $method 【$url】body === $body");
|
||||
|
||||
try {
|
||||
final resp = await _c.request(url,
|
||||
data: body,
|
||||
options: Options(method: method, headers: {
|
||||
"token": AppManager.getUserToken(),
|
||||
"platformtype":'telephone'
|
||||
}));
|
||||
yjPrint("[RequestManager resp]: $method 【$url】body === $resp");
|
||||
|
||||
if (catchError) {
|
||||
if (resp.statusCode == kNeedLoginCode) {
|
||||
AppManager.gotoLogin();
|
||||
return null;
|
||||
}
|
||||
if (resp.statusCode != kSuccessCode) {
|
||||
_alertError("提示", resp.data ?? "未知错误");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return resp.data["data"];
|
||||
} catch (e) {
|
||||
yjPrint("[RequestManager error]: $method 【$url】error === $e");
|
||||
// _alertError("网络错误", "请检查您的网络连接!");
|
||||
if (e is DioException) {
|
||||
DioException de = e;
|
||||
if (de.response?.statusCode == kNeedLoginCode) {
|
||||
AppManager.gotoLogin();
|
||||
return null;
|
||||
}
|
||||
if (de.response?.data is Map) {
|
||||
final data = de.response!.data as Map;
|
||||
Utils.toast(data["message"], AppManager.globalContext!);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static _alertError(String title, String errorText) {
|
||||
Utils.alert(AppManager.globalContext!, errorText, title: title);
|
||||
}
|
||||
}
|
||||
8
lib/common/utils/func_tools.dart
Normal file
8
lib/common/utils/func_tools.dart
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
bool isEmptyString(String? str) {
|
||||
if (str == null || str.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
47
lib/common/utils/utils.dart
Normal file
47
lib/common/utils/utils.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
import 'func_tools.dart';
|
||||
|
||||
class Utils {
|
||||
///大陆手机号码11位数,匹配格式:前三位固定格式+后8位任意数
|
||||
static bool isPhone(String phone) {
|
||||
return RegExp('^1\\d{10}\$').hasMatch(phone);
|
||||
}
|
||||
|
||||
static void toast(String? text, BuildContext? context) {
|
||||
if (isEmptyString(text)) {
|
||||
return;
|
||||
}
|
||||
Fluttertoast.showToast(
|
||||
msg: "$text",
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.CENTER,
|
||||
timeInSecForIosWeb: 1,
|
||||
// backgroundColor: Colors.red,
|
||||
// textColor: Colors.white,
|
||||
fontSize: 16.0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static Future alert(BuildContext context, String? content, {String? title}) {
|
||||
return showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return CupertinoAlertDialog(
|
||||
title: Text(title ?? '提示'),
|
||||
content: Text(content!),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('确定'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
606
lib/data_model/login/login_result.dart
Normal file
606
lib/data_model/login/login_result.dart
Normal file
@@ -0,0 +1,606 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// loginType : "merchant"
|
||||
/// shopName : "双屿pirse(测77)"
|
||||
/// logo : "https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240925/835c45e4729048e8921ba17d021029ec.jpg"
|
||||
/// shopId : 11
|
||||
/// mainId : 11
|
||||
/// user : {"authorities":[{"authority":"tbProductGroup:del"},{"authority":"tbProductGroup:list"},{"authority":"roles:add"},{"authority":"roles:edit"},{"authority":"roles:del"},{"authority":"tbShopTable:list"},{"authority":"roles:list"}],"dataScopes":[18],"roles":["tbProductGroup:del","tbProductGroup:list","roles:add","roles:edit","roles:del","tbShopTable:list","roles:list"],"user":{"avatarName":null,"avatarPath":null,"createBy":"admin","createTime":"2024-05-27 14:10:09","dept":{"id":18,"name":"前厅"},"deptId":null,"email":null,"enabled":true,"gender":null,"id":40,"isAdmin":false,"jobs":[{"id":10,"name":"产品经理"}],"nickName":"双屿pirse(测77)","password":"$2a$10$j414tLJ/fdXXzXriW3y9A.QdP9Ak1.1hiGbvb1.zmQjPc5q.xoipy","phone":"13575788745","pwdResetTime":"2024-08-05 14:18:59","roles":[{"dataScope":"本级","id":2,"level":2,"name":"普通用户"}],"updateBy":"admin","updateTime":"2024-05-27 14:10:09","username":"13718478323"}}
|
||||
/// token : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiI3ZWYzZmU2NWM0ZDU0ZjE5OWU5YmE4NTQ1NmUyZDZiZiIsInVzZXIiOiIxMzcxODQ3ODMyMyIsInNob3BJZCI6IjExIiwic3ViIjoiMTM3MTg0NzgzMjMifQ.9DY1f02WGTJ2e5w1MFJrUQ4KwEvl-QKWsSRALvpYYo6EsA3NercZAN56xL68e8K0eSsArk-9i1LFxb-PtBwmgw"
|
||||
|
||||
LoginResult loginResultFromJson(String str) =>
|
||||
LoginResult.fromJson(json.decode(str));
|
||||
|
||||
String loginResultToJson(LoginResult data) => json.encode(data.toJson());
|
||||
|
||||
class LoginResult {
|
||||
LoginResult({
|
||||
num? loginType,
|
||||
String? shopName,
|
||||
String? logo,
|
||||
num? shopId,
|
||||
num? mainId,
|
||||
User? user,
|
||||
String? token,
|
||||
}) {
|
||||
_loginType = loginType;
|
||||
_shopName = shopName;
|
||||
_logo = logo;
|
||||
_shopId = shopId;
|
||||
_mainId = mainId;
|
||||
_user = user;
|
||||
_token = token;
|
||||
}
|
||||
|
||||
LoginResult.fromJson(dynamic json) {
|
||||
_loginType = json['loginType'];
|
||||
_shopName = json['shopName'];
|
||||
_logo = json['logo'];
|
||||
_shopId = json['shopId'];
|
||||
_mainId = json['mainId'];
|
||||
_user = json['user'] != null ? User.fromJson(json['user']) : null;
|
||||
_token = json['tokenInfo']['tokenValue'];
|
||||
}
|
||||
|
||||
num? _loginType;
|
||||
String? _shopName;
|
||||
String? _logo;
|
||||
num? _shopId;
|
||||
num? _mainId;
|
||||
User? _user;
|
||||
String? _token;
|
||||
|
||||
LoginResult copyWith({
|
||||
num? loginType,
|
||||
String? shopName,
|
||||
String? logo,
|
||||
num? shopId,
|
||||
num? mainId,
|
||||
User? user,
|
||||
String? token,
|
||||
}) =>
|
||||
LoginResult(
|
||||
loginType: loginType ?? _loginType,
|
||||
shopName: shopName ?? _shopName,
|
||||
logo: logo ?? _logo,
|
||||
shopId: shopId ?? _shopId,
|
||||
mainId: mainId ?? _mainId,
|
||||
user: user ?? _user,
|
||||
token: token ?? _token,
|
||||
);
|
||||
|
||||
num? get loginType => _loginType;
|
||||
|
||||
String? get shopName => _shopName;
|
||||
|
||||
String? get logo => _logo;
|
||||
|
||||
num? get shopId => _shopId;
|
||||
|
||||
num? get mainId => _mainId;
|
||||
|
||||
User? get user => _user;
|
||||
|
||||
String? get token => _token;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['loginType'] = _loginType;
|
||||
map['shopName'] = _shopName;
|
||||
map['logo'] = _logo;
|
||||
map['shopId'] = _shopId;
|
||||
map['mainId'] = _mainId;
|
||||
if (_user != null) {
|
||||
map['user'] = _user?.toJson();
|
||||
}
|
||||
map['token'] = _token;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/// authorities : [{"authority":"tbProductGroup:del"},{"authority":"tbProductGroup:list"},{"authority":"roles:add"},{"authority":"roles:edit"},{"authority":"roles:del"},{"authority":"tbShopTable:list"},{"authority":"roles:list"}]
|
||||
/// dataScopes : [18]
|
||||
/// roles : ["tbProductGroup:del","tbProductGroup:list","roles:add","roles:edit","roles:del","tbShopTable:list","roles:list"]
|
||||
/// user : {"avatarName":null,"avatarPath":null,"createBy":"admin","createTime":"2024-05-27 14:10:09","dept":{"id":18,"name":"前厅"},"deptId":null,"email":null,"enabled":true,"gender":null,"id":40,"isAdmin":false,"jobs":[{"id":10,"name":"产品经理"}],"nickName":"双屿pirse(测77)","password":"$2a$10$j414tLJ/fdXXzXriW3y9A.QdP9Ak1.1hiGbvb1.zmQjPc5q.xoipy","phone":"13575788745","pwdResetTime":"2024-08-05 14:18:59","roles":[{"dataScope":"本级","id":2,"level":2,"name":"普通用户"}],"updateBy":"admin","updateTime":"2024-05-27 14:10:09","username":"13718478323"}
|
||||
|
||||
User userFromJson(String str) => User.fromJson(json.decode(str));
|
||||
|
||||
String userToJson(User data) => json.encode(data.toJson());
|
||||
|
||||
class User {
|
||||
User({
|
||||
List<Authorities>? authorities,
|
||||
List<num>? dataScopes,
|
||||
List<String>? roles,
|
||||
UserInfo? userInfo,
|
||||
}) {
|
||||
_authorities = authorities;
|
||||
_dataScopes = dataScopes;
|
||||
_roles = roles;
|
||||
_userInfo = userInfo;
|
||||
}
|
||||
|
||||
User.fromJson(dynamic json) {
|
||||
if (json['authorities'] != null) {
|
||||
_authorities = [];
|
||||
json['authorities'].forEach((v) {
|
||||
_authorities?.add(Authorities.fromJson(v));
|
||||
});
|
||||
}
|
||||
_dataScopes =
|
||||
json['dataScopes'] != null ? json['dataScopes'].cast<num>() : [];
|
||||
_roles = json['roles'] != null ? json['roles'].cast<String>() : [];
|
||||
_userInfo = json['user'] != null ? UserInfo.fromJson(json['user']) : null;
|
||||
}
|
||||
|
||||
List<Authorities>? _authorities;
|
||||
List<num>? _dataScopes;
|
||||
List<String>? _roles;
|
||||
UserInfo? _userInfo;
|
||||
|
||||
User copyWith({
|
||||
List<Authorities>? authorities,
|
||||
List<num>? dataScopes,
|
||||
List<String>? roles,
|
||||
UserInfo? userInfo,
|
||||
}) =>
|
||||
User(
|
||||
authorities: authorities ?? _authorities,
|
||||
dataScopes: dataScopes ?? _dataScopes,
|
||||
roles: roles ?? _roles,
|
||||
userInfo: userInfo ?? _userInfo,
|
||||
);
|
||||
|
||||
List<Authorities>? get authorities => _authorities;
|
||||
|
||||
List<num>? get dataScopes => _dataScopes;
|
||||
|
||||
List<String>? get roles => _roles;
|
||||
|
||||
UserInfo? get userInfo => _userInfo;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
if (_authorities != null) {
|
||||
map['authorities'] = _authorities?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
map['dataScopes'] = _dataScopes;
|
||||
map['roles'] = _roles;
|
||||
if (_userInfo != null) {
|
||||
map['user'] = _userInfo?.toJson();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/// avatarName : null
|
||||
/// avatarPath : null
|
||||
/// createBy : "admin"
|
||||
/// createTime : "2024-05-27 14:10:09"
|
||||
/// dept : {"id":18,"name":"前厅"}
|
||||
/// deptId : null
|
||||
/// email : null
|
||||
/// enabled : true
|
||||
/// gender : null
|
||||
/// id : 40
|
||||
/// isAdmin : false
|
||||
/// jobs : [{"id":10,"name":"产品经理"}]
|
||||
/// nickName : "双屿pirse(测77)"
|
||||
/// password : "$2a$10$j414tLJ/fdXXzXriW3y9A.QdP9Ak1.1hiGbvb1.zmQjPc5q.xoipy"
|
||||
/// phone : "13575788745"
|
||||
/// pwdResetTime : "2024-08-05 14:18:59"
|
||||
/// roles : [{"dataScope":"本级","id":2,"level":2,"name":"普通用户"}]
|
||||
/// updateBy : "admin"
|
||||
/// updateTime : "2024-05-27 14:10:09"
|
||||
/// username : "13718478323"
|
||||
|
||||
User userInfoFromJson(String str) => User.fromJson(json.decode(str));
|
||||
|
||||
String userInfoToJson(User data) => json.encode(data.toJson());
|
||||
|
||||
class UserInfo {
|
||||
UserInfo({
|
||||
dynamic avatarName,
|
||||
dynamic avatarPath,
|
||||
String? createBy,
|
||||
String? createTime,
|
||||
Dept? dept,
|
||||
dynamic deptId,
|
||||
dynamic email,
|
||||
bool? enabled,
|
||||
dynamic gender,
|
||||
num? id,
|
||||
bool? isAdmin,
|
||||
List<Jobs>? jobs,
|
||||
String? nickName,
|
||||
String? password,
|
||||
String? phone,
|
||||
String? pwdResetTime,
|
||||
List<Roles>? roles,
|
||||
String? updateBy,
|
||||
String? updateTime,
|
||||
String? username,
|
||||
}) {
|
||||
_avatarName = avatarName;
|
||||
_avatarPath = avatarPath;
|
||||
_createBy = createBy;
|
||||
_createTime = createTime;
|
||||
_dept = dept;
|
||||
_deptId = deptId;
|
||||
_email = email;
|
||||
_enabled = enabled;
|
||||
_gender = gender;
|
||||
_id = id;
|
||||
_isAdmin = isAdmin;
|
||||
_jobs = jobs;
|
||||
_nickName = nickName;
|
||||
_password = password;
|
||||
_phone = phone;
|
||||
_pwdResetTime = pwdResetTime;
|
||||
_roles = roles;
|
||||
_updateBy = updateBy;
|
||||
_updateTime = updateTime;
|
||||
_username = username;
|
||||
}
|
||||
|
||||
UserInfo.fromJson(dynamic json) {
|
||||
_avatarName = json['avatarName'];
|
||||
_avatarPath = json['avatarPath'];
|
||||
_createBy = json['createBy'];
|
||||
_createTime = json['createTime'];
|
||||
_dept = json['dept'] != null ? Dept.fromJson(json['dept']) : null;
|
||||
_deptId = json['deptId'];
|
||||
_email = json['email'];
|
||||
_enabled = json['enabled'];
|
||||
_gender = json['gender'];
|
||||
_id = json['id'];
|
||||
_isAdmin = json['isAdmin'];
|
||||
if (json['jobs'] != null) {
|
||||
_jobs = [];
|
||||
json['jobs'].forEach((v) {
|
||||
_jobs?.add(Jobs.fromJson(v));
|
||||
});
|
||||
}
|
||||
_nickName = json['nickName'];
|
||||
_password = json['password'];
|
||||
_phone = json['phone'];
|
||||
_pwdResetTime = json['pwdResetTime'];
|
||||
if (json['roles'] != null) {
|
||||
_roles = [];
|
||||
json['roles'].forEach((v) {
|
||||
_roles?.add(Roles.fromJson(v));
|
||||
});
|
||||
}
|
||||
_updateBy = json['updateBy'];
|
||||
_updateTime = json['updateTime'];
|
||||
_username = json['username'];
|
||||
}
|
||||
|
||||
dynamic _avatarName;
|
||||
dynamic _avatarPath;
|
||||
String? _createBy;
|
||||
String? _createTime;
|
||||
Dept? _dept;
|
||||
dynamic _deptId;
|
||||
dynamic _email;
|
||||
bool? _enabled;
|
||||
dynamic _gender;
|
||||
num? _id;
|
||||
bool? _isAdmin;
|
||||
List<Jobs>? _jobs;
|
||||
String? _nickName;
|
||||
String? _password;
|
||||
String? _phone;
|
||||
String? _pwdResetTime;
|
||||
List<Roles>? _roles;
|
||||
String? _updateBy;
|
||||
String? _updateTime;
|
||||
String? _username;
|
||||
|
||||
UserInfo copyWith({
|
||||
dynamic avatarName,
|
||||
dynamic avatarPath,
|
||||
String? createBy,
|
||||
String? createTime,
|
||||
Dept? dept,
|
||||
dynamic deptId,
|
||||
dynamic email,
|
||||
bool? enabled,
|
||||
dynamic gender,
|
||||
num? id,
|
||||
bool? isAdmin,
|
||||
List<Jobs>? jobs,
|
||||
String? nickName,
|
||||
String? password,
|
||||
String? phone,
|
||||
String? pwdResetTime,
|
||||
List<Roles>? roles,
|
||||
String? updateBy,
|
||||
String? updateTime,
|
||||
String? username,
|
||||
}) =>
|
||||
UserInfo(
|
||||
avatarName: avatarName ?? _avatarName,
|
||||
avatarPath: avatarPath ?? _avatarPath,
|
||||
createBy: createBy ?? _createBy,
|
||||
createTime: createTime ?? _createTime,
|
||||
dept: dept ?? _dept,
|
||||
deptId: deptId ?? _deptId,
|
||||
email: email ?? _email,
|
||||
enabled: enabled ?? _enabled,
|
||||
gender: gender ?? _gender,
|
||||
id: id ?? _id,
|
||||
isAdmin: isAdmin ?? _isAdmin,
|
||||
jobs: jobs ?? _jobs,
|
||||
nickName: nickName ?? _nickName,
|
||||
password: password ?? _password,
|
||||
phone: phone ?? _phone,
|
||||
pwdResetTime: pwdResetTime ?? _pwdResetTime,
|
||||
roles: roles ?? _roles,
|
||||
updateBy: updateBy ?? _updateBy,
|
||||
updateTime: updateTime ?? _updateTime,
|
||||
username: username ?? _username,
|
||||
);
|
||||
|
||||
dynamic get avatarName => _avatarName;
|
||||
|
||||
dynamic get avatarPath => _avatarPath;
|
||||
|
||||
String? get createBy => _createBy;
|
||||
|
||||
String? get createTime => _createTime;
|
||||
|
||||
Dept? get dept => _dept;
|
||||
|
||||
dynamic get deptId => _deptId;
|
||||
|
||||
dynamic get email => _email;
|
||||
|
||||
bool? get enabled => _enabled;
|
||||
|
||||
dynamic get gender => _gender;
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
bool? get isAdmin => _isAdmin;
|
||||
|
||||
List<Jobs>? get jobs => _jobs;
|
||||
|
||||
String? get nickName => _nickName;
|
||||
|
||||
String? get password => _password;
|
||||
|
||||
String? get phone => _phone;
|
||||
|
||||
String? get pwdResetTime => _pwdResetTime;
|
||||
|
||||
List<Roles>? get roles => _roles;
|
||||
|
||||
String? get updateBy => _updateBy;
|
||||
|
||||
String? get updateTime => _updateTime;
|
||||
|
||||
String? get username => _username;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['avatarName'] = _avatarName;
|
||||
map['avatarPath'] = _avatarPath;
|
||||
map['createBy'] = _createBy;
|
||||
map['createTime'] = _createTime;
|
||||
if (_dept != null) {
|
||||
map['dept'] = _dept?.toJson();
|
||||
}
|
||||
map['deptId'] = _deptId;
|
||||
map['email'] = _email;
|
||||
map['enabled'] = _enabled;
|
||||
map['gender'] = _gender;
|
||||
map['id'] = _id;
|
||||
map['isAdmin'] = _isAdmin;
|
||||
if (_jobs != null) {
|
||||
map['jobs'] = _jobs?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
map['nickName'] = _nickName;
|
||||
map['password'] = _password;
|
||||
map['phone'] = _phone;
|
||||
map['pwdResetTime'] = _pwdResetTime;
|
||||
if (_roles != null) {
|
||||
map['roles'] = _roles?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
map['updateBy'] = _updateBy;
|
||||
map['updateTime'] = _updateTime;
|
||||
map['username'] = _username;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/// dataScope : "本级"
|
||||
/// id : 2
|
||||
/// level : 2
|
||||
/// name : "普通用户"
|
||||
|
||||
Roles rolesFromJson(String str) => Roles.fromJson(json.decode(str));
|
||||
|
||||
String rolesToJson(Roles data) => json.encode(data.toJson());
|
||||
|
||||
class Roles {
|
||||
Roles({
|
||||
String? dataScope,
|
||||
num? id,
|
||||
num? level,
|
||||
String? name,
|
||||
}) {
|
||||
_dataScope = dataScope;
|
||||
_id = id;
|
||||
_level = level;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
Roles.fromJson(dynamic json) {
|
||||
_dataScope = json['dataScope'];
|
||||
_id = json['id'];
|
||||
_level = json['level'];
|
||||
_name = json['name'];
|
||||
}
|
||||
|
||||
String? _dataScope;
|
||||
num? _id;
|
||||
num? _level;
|
||||
String? _name;
|
||||
|
||||
Roles copyWith({
|
||||
String? dataScope,
|
||||
num? id,
|
||||
num? level,
|
||||
String? name,
|
||||
}) =>
|
||||
Roles(
|
||||
dataScope: dataScope ?? _dataScope,
|
||||
id: id ?? _id,
|
||||
level: level ?? _level,
|
||||
name: name ?? _name,
|
||||
);
|
||||
|
||||
String? get dataScope => _dataScope;
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
num? get level => _level;
|
||||
|
||||
String? get name => _name;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['dataScope'] = _dataScope;
|
||||
map['id'] = _id;
|
||||
map['level'] = _level;
|
||||
map['name'] = _name;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/// id : 10
|
||||
/// name : "产品经理"
|
||||
|
||||
Jobs jobsFromJson(String str) => Jobs.fromJson(json.decode(str));
|
||||
|
||||
String jobsToJson(Jobs data) => json.encode(data.toJson());
|
||||
|
||||
class Jobs {
|
||||
Jobs({
|
||||
num? id,
|
||||
String? name,
|
||||
}) {
|
||||
_id = id;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
Jobs.fromJson(dynamic json) {
|
||||
_id = json['id'];
|
||||
_name = json['name'];
|
||||
}
|
||||
|
||||
num? _id;
|
||||
String? _name;
|
||||
|
||||
Jobs copyWith({
|
||||
num? id,
|
||||
String? name,
|
||||
}) =>
|
||||
Jobs(
|
||||
id: id ?? _id,
|
||||
name: name ?? _name,
|
||||
);
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
String? get name => _name;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = _id;
|
||||
map['name'] = _name;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/// id : 18
|
||||
/// name : "前厅"
|
||||
|
||||
Dept deptFromJson(String str) => Dept.fromJson(json.decode(str));
|
||||
|
||||
String deptToJson(Dept data) => json.encode(data.toJson());
|
||||
|
||||
class Dept {
|
||||
Dept({
|
||||
num? id,
|
||||
String? name,
|
||||
}) {
|
||||
_id = id;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
Dept.fromJson(dynamic json) {
|
||||
_id = json['id'];
|
||||
_name = json['name'];
|
||||
}
|
||||
|
||||
num? _id;
|
||||
String? _name;
|
||||
|
||||
Dept copyWith({
|
||||
num? id,
|
||||
String? name,
|
||||
}) =>
|
||||
Dept(
|
||||
id: id ?? _id,
|
||||
name: name ?? _name,
|
||||
);
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
String? get name => _name;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = _id;
|
||||
map['name'] = _name;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/// authority : "tbProductGroup:del"
|
||||
|
||||
Authorities authoritiesFromJson(String str) =>
|
||||
Authorities.fromJson(json.decode(str));
|
||||
|
||||
String authoritiesToJson(Authorities data) => json.encode(data.toJson());
|
||||
|
||||
class Authorities {
|
||||
Authorities({
|
||||
String? authority,
|
||||
}) {
|
||||
_authority = authority;
|
||||
}
|
||||
|
||||
Authorities.fromJson(dynamic json) {
|
||||
_authority = json['authority'];
|
||||
}
|
||||
|
||||
String? _authority;
|
||||
|
||||
Authorities copyWith({
|
||||
String? authority,
|
||||
}) =>
|
||||
Authorities(
|
||||
authority: authority ?? _authority,
|
||||
);
|
||||
|
||||
String? get authority => _authority;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['authority'] = _authority;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
58
lib/data_model/reserve/reserve_log_model.dart
Normal file
58
lib/data_model/reserve/reserve_log_model.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// consumeOrders : 0
|
||||
/// cancelOrders : 0
|
||||
/// phoneNumber : "10086"
|
||||
|
||||
ReserveLogModel reserveLogModelFromJson(String str) =>
|
||||
ReserveLogModel.fromJson(json.decode(str));
|
||||
|
||||
String reserveLogModelToJson(ReserveLogModel data) =>
|
||||
json.encode(data.toJson());
|
||||
|
||||
class ReserveLogModel {
|
||||
ReserveLogModel({
|
||||
num? consumeOrders,
|
||||
num? cancelOrders,
|
||||
String? phoneNumber,
|
||||
}) {
|
||||
_consumeOrders = consumeOrders;
|
||||
_cancelOrders = cancelOrders;
|
||||
_phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
ReserveLogModel.fromJson(dynamic json) {
|
||||
_consumeOrders = json['consumeOrders'];
|
||||
_cancelOrders = json['cancelOrders'];
|
||||
_phoneNumber = json['phoneNumber'];
|
||||
}
|
||||
|
||||
num? _consumeOrders;
|
||||
num? _cancelOrders;
|
||||
String? _phoneNumber;
|
||||
|
||||
ReserveLogModel copyWith({
|
||||
num? consumeOrders,
|
||||
num? cancelOrders,
|
||||
String? phoneNumber,
|
||||
}) =>
|
||||
ReserveLogModel(
|
||||
consumeOrders: consumeOrders ?? _consumeOrders,
|
||||
cancelOrders: cancelOrders ?? _cancelOrders,
|
||||
phoneNumber: phoneNumber ?? _phoneNumber,
|
||||
);
|
||||
|
||||
num? get consumeOrders => _consumeOrders;
|
||||
|
||||
num? get cancelOrders => _cancelOrders;
|
||||
|
||||
String? get phoneNumber => _phoneNumber;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['consumeOrders'] = _consumeOrders;
|
||||
map['cancelOrders'] = _cancelOrders;
|
||||
map['phoneNumber'] = _phoneNumber;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
117
lib/data_model/reserve/table_area_model.dart
Normal file
117
lib/data_model/reserve/table_area_model.dart
Normal file
@@ -0,0 +1,117 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// capacityRange : null
|
||||
/// createdAt : 1716791935255
|
||||
/// id : 20
|
||||
/// name : "大厅"
|
||||
/// price : null
|
||||
/// shopId : 11
|
||||
/// sort : 0
|
||||
/// updatedAt : 1716791935255
|
||||
/// view : null
|
||||
|
||||
TableAreaModel tableAreaModelFromJson(String str) =>
|
||||
TableAreaModel.fromJson(json.decode(str));
|
||||
|
||||
String tableAreaModelToJson(TableAreaModel data) => json.encode(data.toJson());
|
||||
|
||||
class TableAreaModel {
|
||||
TableAreaModel({
|
||||
dynamic capacityRange,
|
||||
num? createdAt,
|
||||
num? id,
|
||||
String? name,
|
||||
dynamic price,
|
||||
num? shopId,
|
||||
num? sort,
|
||||
num? updatedAt,
|
||||
dynamic view,
|
||||
}) {
|
||||
_capacityRange = capacityRange;
|
||||
_createdAt = createdAt;
|
||||
_id = id;
|
||||
_name = name;
|
||||
_price = price;
|
||||
_shopId = shopId;
|
||||
_sort = sort;
|
||||
_updatedAt = updatedAt;
|
||||
_view = view;
|
||||
}
|
||||
|
||||
TableAreaModel.fromJson(dynamic json) {
|
||||
_capacityRange = json['capacityRange'];
|
||||
_createdAt = json['createdAt'];
|
||||
_id = json['id'];
|
||||
_name = json['name'];
|
||||
_price = json['price'];
|
||||
_shopId = json['shopId'];
|
||||
_sort = json['sort'];
|
||||
_updatedAt = json['updatedAt'];
|
||||
_view = json['view'];
|
||||
}
|
||||
|
||||
dynamic _capacityRange;
|
||||
num? _createdAt;
|
||||
num? _id;
|
||||
String? _name;
|
||||
dynamic _price;
|
||||
num? _shopId;
|
||||
num? _sort;
|
||||
num? _updatedAt;
|
||||
dynamic _view;
|
||||
|
||||
TableAreaModel copyWith({
|
||||
dynamic capacityRange,
|
||||
num? createdAt,
|
||||
num? id,
|
||||
String? name,
|
||||
dynamic price,
|
||||
num? shopId,
|
||||
num? sort,
|
||||
num? updatedAt,
|
||||
dynamic view,
|
||||
}) =>
|
||||
TableAreaModel(
|
||||
capacityRange: capacityRange ?? _capacityRange,
|
||||
createdAt: createdAt ?? _createdAt,
|
||||
id: id ?? _id,
|
||||
name: name ?? _name,
|
||||
price: price ?? _price,
|
||||
shopId: shopId ?? _shopId,
|
||||
sort: sort ?? _sort,
|
||||
updatedAt: updatedAt ?? _updatedAt,
|
||||
view: view ?? _view,
|
||||
);
|
||||
|
||||
dynamic get capacityRange => _capacityRange;
|
||||
|
||||
num? get createdAt => _createdAt;
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
String? get name => _name;
|
||||
|
||||
dynamic get price => _price;
|
||||
|
||||
num? get shopId => _shopId;
|
||||
|
||||
num? get sort => _sort;
|
||||
|
||||
num? get updatedAt => _updatedAt;
|
||||
|
||||
dynamic get view => _view;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['capacityRange'] = _capacityRange;
|
||||
map['createdAt'] = _createdAt;
|
||||
map['id'] = _id;
|
||||
map['name'] = _name;
|
||||
map['price'] = _price;
|
||||
map['shopId'] = _shopId;
|
||||
map['sort'] = _sort;
|
||||
map['updatedAt'] = _updatedAt;
|
||||
map['view'] = _view;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
547
lib/data_model/reserve/table_model.dart
Normal file
547
lib/data_model/reserve/table_model.dart
Normal file
@@ -0,0 +1,547 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// amount : 0.00
|
||||
/// areaId : 20
|
||||
/// autoClear : 0
|
||||
/// bookingInfo : {"arrivedTime":null,"bookingDate":"2024-11-22","bookingPerson":"王","bookingTableNum":0,"bookingTime":"2024-11-22 18:30:00","bookingType":"dinner","createTime":"2024-11-22 16:16:17","createUserName":"双屿pirse(测77)","delFlag":0,"dinerNum":6,"diningStandardPrice":0.00,"diningStandardUnit":"table","diningType":"普通用餐","focus":1,"gender":1,"id":1,"orderNo":"BK241122161617-cwba","phoneNumber":"13011223344","receiveMarketingSms":1,"remark":"今个高兴,和哥几个一醉方休","shopId":11,"shopTableId":151,"status":20,"timeoutMinute":60,"updateTime":"2024-11-22 16:27:47"}
|
||||
/// createdAt : 1726283309239
|
||||
/// endTime : "2024-11-06 18:03:33"
|
||||
/// id : 151
|
||||
/// isPredate : 1
|
||||
/// maxCapacity : 4
|
||||
/// name : "A1"
|
||||
/// perhour : null
|
||||
/// predateAmount : 0.00
|
||||
/// productNum : 6
|
||||
/// qrcode : "3000101848"
|
||||
/// realAmount : 33.70
|
||||
/// shopId : 11
|
||||
/// sort : 0
|
||||
/// status : "using"
|
||||
/// totalAmount : 33.70
|
||||
/// type : 0
|
||||
/// updatedAt : 1728720069117
|
||||
/// useNum : 1
|
||||
/// useTime : "2024-11-22 16:28:59"
|
||||
/// view : null
|
||||
|
||||
TableModel tableModelFromJson(String str) =>
|
||||
TableModel.fromJson(json.decode(str));
|
||||
|
||||
String tableModelToJson(TableModel data) => json.encode(data.toJson());
|
||||
|
||||
class TableModel {
|
||||
TableModel({
|
||||
num? amount,
|
||||
num? areaId,
|
||||
num? autoClear,
|
||||
BookingInfo? bookingInfo,
|
||||
num? createdAt,
|
||||
String? endTime,
|
||||
num? id,
|
||||
num? isPredate,
|
||||
num? maxCapacity,
|
||||
String? name,
|
||||
dynamic perhour,
|
||||
num? predateAmount,
|
||||
num? productNum,
|
||||
String? qrcode,
|
||||
num? realAmount,
|
||||
num? shopId,
|
||||
num? sort,
|
||||
String? status,
|
||||
num? totalAmount,
|
||||
num? type,
|
||||
num? updatedAt,
|
||||
num? useNum,
|
||||
String? useTime,
|
||||
dynamic view,
|
||||
}) {
|
||||
_amount = amount;
|
||||
_areaId = areaId;
|
||||
_autoClear = autoClear;
|
||||
_bookingInfo = bookingInfo;
|
||||
_createdAt = createdAt;
|
||||
_endTime = endTime;
|
||||
_id = id;
|
||||
_isPredate = isPredate;
|
||||
_maxCapacity = maxCapacity;
|
||||
_name = name;
|
||||
_perhour = perhour;
|
||||
_predateAmount = predateAmount;
|
||||
_productNum = productNum;
|
||||
_qrcode = qrcode;
|
||||
_realAmount = realAmount;
|
||||
_shopId = shopId;
|
||||
_sort = sort;
|
||||
_status = status;
|
||||
_totalAmount = totalAmount;
|
||||
_type = type;
|
||||
_updatedAt = updatedAt;
|
||||
_useNum = useNum;
|
||||
_useTime = useTime;
|
||||
_view = view;
|
||||
}
|
||||
|
||||
TableModel.fromJson(dynamic json) {
|
||||
_amount = json['amount'];
|
||||
_areaId = json['areaId'];
|
||||
_autoClear = json['autoClear'];
|
||||
_bookingInfo = json['bookingInfo'] != null
|
||||
? BookingInfo.fromJson(json['bookingInfo'])
|
||||
: null;
|
||||
_createdAt = json['createdAt'];
|
||||
_endTime = json['endTime'];
|
||||
_id = json['id'];
|
||||
_isPredate = json['isPredate'];
|
||||
_maxCapacity = json['maxCapacity'];
|
||||
_name = json['name'];
|
||||
_perhour = json['perhour'];
|
||||
_predateAmount = json['predateAmount'];
|
||||
_productNum = json['productNum'];
|
||||
_qrcode = json['qrcode'];
|
||||
_realAmount = json['realAmount'];
|
||||
_shopId = json['shopId'];
|
||||
_sort = json['sort'];
|
||||
_status = json['status'];
|
||||
_totalAmount = json['totalAmount'];
|
||||
_type = json['type'];
|
||||
_updatedAt = json['updatedAt'];
|
||||
_useNum = json['useNum'];
|
||||
_useTime = json['useTime'];
|
||||
_view = json['view'];
|
||||
}
|
||||
|
||||
num? _amount;
|
||||
num? _areaId;
|
||||
num? _autoClear;
|
||||
BookingInfo? _bookingInfo;
|
||||
num? _createdAt;
|
||||
String? _endTime;
|
||||
num? _id;
|
||||
num? _isPredate;
|
||||
num? _maxCapacity;
|
||||
String? _name;
|
||||
dynamic _perhour;
|
||||
num? _predateAmount;
|
||||
num? _productNum;
|
||||
String? _qrcode;
|
||||
num? _realAmount;
|
||||
num? _shopId;
|
||||
num? _sort;
|
||||
String? _status;
|
||||
num? _totalAmount;
|
||||
num? _type;
|
||||
num? _updatedAt;
|
||||
num? _useNum;
|
||||
String? _useTime;
|
||||
dynamic _view;
|
||||
|
||||
TableModel copyWith({
|
||||
num? amount,
|
||||
num? areaId,
|
||||
num? autoClear,
|
||||
BookingInfo? bookingInfo,
|
||||
num? createdAt,
|
||||
String? endTime,
|
||||
num? id,
|
||||
num? isPredate,
|
||||
num? maxCapacity,
|
||||
String? name,
|
||||
dynamic perhour,
|
||||
num? predateAmount,
|
||||
num? productNum,
|
||||
String? qrcode,
|
||||
num? realAmount,
|
||||
num? shopId,
|
||||
num? sort,
|
||||
String? status,
|
||||
num? totalAmount,
|
||||
num? type,
|
||||
num? updatedAt,
|
||||
num? useNum,
|
||||
String? useTime,
|
||||
dynamic view,
|
||||
}) =>
|
||||
TableModel(
|
||||
amount: amount ?? _amount,
|
||||
areaId: areaId ?? _areaId,
|
||||
autoClear: autoClear ?? _autoClear,
|
||||
bookingInfo: bookingInfo ?? _bookingInfo,
|
||||
createdAt: createdAt ?? _createdAt,
|
||||
endTime: endTime ?? _endTime,
|
||||
id: id ?? _id,
|
||||
isPredate: isPredate ?? _isPredate,
|
||||
maxCapacity: maxCapacity ?? _maxCapacity,
|
||||
name: name ?? _name,
|
||||
perhour: perhour ?? _perhour,
|
||||
predateAmount: predateAmount ?? _predateAmount,
|
||||
productNum: productNum ?? _productNum,
|
||||
qrcode: qrcode ?? _qrcode,
|
||||
realAmount: realAmount ?? _realAmount,
|
||||
shopId: shopId ?? _shopId,
|
||||
sort: sort ?? _sort,
|
||||
status: status ?? _status,
|
||||
totalAmount: totalAmount ?? _totalAmount,
|
||||
type: type ?? _type,
|
||||
updatedAt: updatedAt ?? _updatedAt,
|
||||
useNum: useNum ?? _useNum,
|
||||
useTime: useTime ?? _useTime,
|
||||
view: view ?? _view,
|
||||
);
|
||||
|
||||
num? get amount => _amount;
|
||||
|
||||
num? get areaId => _areaId;
|
||||
|
||||
num? get autoClear => _autoClear;
|
||||
|
||||
BookingInfo? get bookingInfo => _bookingInfo;
|
||||
|
||||
num? get createdAt => _createdAt;
|
||||
|
||||
String? get endTime => _endTime;
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
num? get isPredate => _isPredate;
|
||||
|
||||
num? get maxCapacity => _maxCapacity;
|
||||
|
||||
String? get name => _name;
|
||||
|
||||
dynamic get perhour => _perhour;
|
||||
|
||||
num? get predateAmount => _predateAmount;
|
||||
|
||||
num? get productNum => _productNum;
|
||||
|
||||
String? get qrcode => _qrcode;
|
||||
|
||||
num? get realAmount => _realAmount;
|
||||
|
||||
num? get shopId => _shopId;
|
||||
|
||||
num? get sort => _sort;
|
||||
|
||||
String? get status => _status;
|
||||
|
||||
num? get totalAmount => _totalAmount;
|
||||
|
||||
num? get type => _type;
|
||||
|
||||
num? get updatedAt => _updatedAt;
|
||||
|
||||
num? get useNum => _useNum;
|
||||
|
||||
String? get useTime => _useTime;
|
||||
|
||||
dynamic get view => _view;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['amount'] = _amount;
|
||||
map['areaId'] = _areaId;
|
||||
map['autoClear'] = _autoClear;
|
||||
if (_bookingInfo != null) {
|
||||
map['bookingInfo'] = _bookingInfo?.toJson();
|
||||
}
|
||||
map['createdAt'] = _createdAt;
|
||||
map['endTime'] = _endTime;
|
||||
map['id'] = _id;
|
||||
map['isPredate'] = _isPredate;
|
||||
map['maxCapacity'] = _maxCapacity;
|
||||
map['name'] = _name;
|
||||
map['perhour'] = _perhour;
|
||||
map['predateAmount'] = _predateAmount;
|
||||
map['productNum'] = _productNum;
|
||||
map['qrcode'] = _qrcode;
|
||||
map['realAmount'] = _realAmount;
|
||||
map['shopId'] = _shopId;
|
||||
map['sort'] = _sort;
|
||||
map['status'] = _status;
|
||||
map['totalAmount'] = _totalAmount;
|
||||
map['type'] = _type;
|
||||
map['updatedAt'] = _updatedAt;
|
||||
map['useNum'] = _useNum;
|
||||
map['useTime'] = _useTime;
|
||||
map['view'] = _view;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/// arrivedTime : null
|
||||
/// bookingDate : "2024-11-22"
|
||||
/// bookingPerson : "王"
|
||||
/// bookingTableNum : 0
|
||||
/// bookingTime : "2024-11-22 18:30:00"
|
||||
/// bookingType : "dinner"
|
||||
/// createTime : "2024-11-22 16:16:17"
|
||||
/// createUserName : "双屿pirse(测77)"
|
||||
/// delFlag : 0
|
||||
/// dinerNum : 6
|
||||
/// diningStandardPrice : 0.00
|
||||
/// diningStandardUnit : "table"
|
||||
/// diningType : "普通用餐"
|
||||
/// focus : 1
|
||||
/// gender : 1
|
||||
/// id : 1
|
||||
/// orderNo : "BK241122161617-cwba"
|
||||
/// phoneNumber : "13011223344"
|
||||
/// receiveMarketingSms : 1
|
||||
/// remark : "今个高兴,和哥几个一醉方休"
|
||||
/// shopId : 11
|
||||
/// shopTableId : 151
|
||||
/// status : 20
|
||||
/// timeoutMinute : 60
|
||||
/// updateTime : "2024-11-22 16:27:47"
|
||||
|
||||
BookingInfo bookingInfoFromJson(String str) =>
|
||||
BookingInfo.fromJson(json.decode(str));
|
||||
|
||||
String bookingInfoToJson(BookingInfo data) => json.encode(data.toJson());
|
||||
|
||||
class BookingInfo {
|
||||
BookingInfo({
|
||||
dynamic arrivedTime,
|
||||
String? bookingDate,
|
||||
String? bookingPerson,
|
||||
num? bookingTableNum,
|
||||
String? bookingTime,
|
||||
String? bookingType,
|
||||
String? createTime,
|
||||
String? createUserName,
|
||||
num? delFlag,
|
||||
num? dinerNum,
|
||||
num? diningStandardPrice,
|
||||
String? diningStandardUnit,
|
||||
String? diningType,
|
||||
num? focus,
|
||||
num? gender,
|
||||
num? id,
|
||||
String? orderNo,
|
||||
String? phoneNumber,
|
||||
num? receiveMarketingSms,
|
||||
String? remark,
|
||||
num? shopId,
|
||||
num? shopTableId,
|
||||
num? status,
|
||||
num? timeoutMinute,
|
||||
String? updateTime,
|
||||
}) {
|
||||
_arrivedTime = arrivedTime;
|
||||
_bookingDate = bookingDate;
|
||||
_bookingPerson = bookingPerson;
|
||||
_bookingTableNum = bookingTableNum;
|
||||
_bookingTime = bookingTime;
|
||||
_bookingType = bookingType;
|
||||
_createTime = createTime;
|
||||
_createUserName = createUserName;
|
||||
_delFlag = delFlag;
|
||||
_dinerNum = dinerNum;
|
||||
_diningStandardPrice = diningStandardPrice;
|
||||
_diningStandardUnit = diningStandardUnit;
|
||||
_diningType = diningType;
|
||||
_focus = focus;
|
||||
_gender = gender;
|
||||
_id = id;
|
||||
_orderNo = orderNo;
|
||||
_phoneNumber = phoneNumber;
|
||||
_receiveMarketingSms = receiveMarketingSms;
|
||||
_remark = remark;
|
||||
_shopId = shopId;
|
||||
_shopTableId = shopTableId;
|
||||
_status = status;
|
||||
_timeoutMinute = timeoutMinute;
|
||||
_updateTime = updateTime;
|
||||
}
|
||||
|
||||
BookingInfo.fromJson(dynamic json) {
|
||||
_arrivedTime = json['arrivedTime'];
|
||||
_bookingDate = json['bookingDate'];
|
||||
_bookingPerson = json['bookingPerson'];
|
||||
_bookingTableNum = json['bookingTableNum'];
|
||||
_bookingTime = json['bookingTime'];
|
||||
_bookingType = json['bookingType'];
|
||||
_createTime = json['createTime'];
|
||||
_createUserName = json['createUserName'];
|
||||
_delFlag = json['delFlag'];
|
||||
_dinerNum = json['dinerNum'];
|
||||
_diningStandardPrice = json['diningStandardPrice'];
|
||||
_diningStandardUnit = json['diningStandardUnit'];
|
||||
_diningType = json['diningType'];
|
||||
_focus = json['focus'];
|
||||
_gender = json['gender'];
|
||||
_id = json['id'];
|
||||
_orderNo = json['orderNo'];
|
||||
_phoneNumber = json['phoneNumber'];
|
||||
_receiveMarketingSms = json['receiveMarketingSms'];
|
||||
_remark = json['remark'];
|
||||
_shopId = json['shopId'];
|
||||
_shopTableId = json['shopTableId'];
|
||||
_status = json['status'];
|
||||
_timeoutMinute = json['timeoutMinute'];
|
||||
_updateTime = json['updateTime'];
|
||||
}
|
||||
|
||||
dynamic _arrivedTime;
|
||||
String? _bookingDate;
|
||||
String? _bookingPerson;
|
||||
num? _bookingTableNum;
|
||||
String? _bookingTime;
|
||||
String? _bookingType;
|
||||
String? _createTime;
|
||||
String? _createUserName;
|
||||
num? _delFlag;
|
||||
num? _dinerNum;
|
||||
num? _diningStandardPrice;
|
||||
String? _diningStandardUnit;
|
||||
String? _diningType;
|
||||
num? _focus;
|
||||
num? _gender;
|
||||
num? _id;
|
||||
String? _orderNo;
|
||||
String? _phoneNumber;
|
||||
num? _receiveMarketingSms;
|
||||
String? _remark;
|
||||
num? _shopId;
|
||||
num? _shopTableId;
|
||||
num? _status;
|
||||
num? _timeoutMinute;
|
||||
String? _updateTime;
|
||||
|
||||
BookingInfo copyWith({
|
||||
dynamic arrivedTime,
|
||||
String? bookingDate,
|
||||
String? bookingPerson,
|
||||
num? bookingTableNum,
|
||||
String? bookingTime,
|
||||
String? bookingType,
|
||||
String? createTime,
|
||||
String? createUserName,
|
||||
num? delFlag,
|
||||
num? dinerNum,
|
||||
num? diningStandardPrice,
|
||||
String? diningStandardUnit,
|
||||
String? diningType,
|
||||
num? focus,
|
||||
num? gender,
|
||||
num? id,
|
||||
String? orderNo,
|
||||
String? phoneNumber,
|
||||
num? receiveMarketingSms,
|
||||
String? remark,
|
||||
num? shopId,
|
||||
num? shopTableId,
|
||||
num? status,
|
||||
num? timeoutMinute,
|
||||
String? updateTime,
|
||||
}) =>
|
||||
BookingInfo(
|
||||
arrivedTime: arrivedTime ?? _arrivedTime,
|
||||
bookingDate: bookingDate ?? _bookingDate,
|
||||
bookingPerson: bookingPerson ?? _bookingPerson,
|
||||
bookingTableNum: bookingTableNum ?? _bookingTableNum,
|
||||
bookingTime: bookingTime ?? _bookingTime,
|
||||
bookingType: bookingType ?? _bookingType,
|
||||
createTime: createTime ?? _createTime,
|
||||
createUserName: createUserName ?? _createUserName,
|
||||
delFlag: delFlag ?? _delFlag,
|
||||
dinerNum: dinerNum ?? _dinerNum,
|
||||
diningStandardPrice: diningStandardPrice ?? _diningStandardPrice,
|
||||
diningStandardUnit: diningStandardUnit ?? _diningStandardUnit,
|
||||
diningType: diningType ?? _diningType,
|
||||
focus: focus ?? _focus,
|
||||
gender: gender ?? _gender,
|
||||
id: id ?? _id,
|
||||
orderNo: orderNo ?? _orderNo,
|
||||
phoneNumber: phoneNumber ?? _phoneNumber,
|
||||
receiveMarketingSms: receiveMarketingSms ?? _receiveMarketingSms,
|
||||
remark: remark ?? _remark,
|
||||
shopId: shopId ?? _shopId,
|
||||
shopTableId: shopTableId ?? _shopTableId,
|
||||
status: status ?? _status,
|
||||
timeoutMinute: timeoutMinute ?? _timeoutMinute,
|
||||
updateTime: updateTime ?? _updateTime,
|
||||
);
|
||||
|
||||
dynamic get arrivedTime => _arrivedTime;
|
||||
|
||||
String? get bookingDate => _bookingDate;
|
||||
|
||||
String? get bookingPerson => _bookingPerson;
|
||||
|
||||
num? get bookingTableNum => _bookingTableNum;
|
||||
|
||||
String? get bookingTime => _bookingTime;
|
||||
|
||||
String? get bookingType => _bookingType;
|
||||
|
||||
String? get createTime => _createTime;
|
||||
|
||||
String? get createUserName => _createUserName;
|
||||
|
||||
num? get delFlag => _delFlag;
|
||||
|
||||
num? get dinerNum => _dinerNum;
|
||||
|
||||
num? get diningStandardPrice => _diningStandardPrice;
|
||||
|
||||
String? get diningStandardUnit => _diningStandardUnit;
|
||||
|
||||
String? get diningType => _diningType;
|
||||
|
||||
num? get focus => _focus;
|
||||
|
||||
num? get gender => _gender;
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
String? get orderNo => _orderNo;
|
||||
|
||||
String? get phoneNumber => _phoneNumber;
|
||||
|
||||
num? get receiveMarketingSms => _receiveMarketingSms;
|
||||
|
||||
String? get remark => _remark;
|
||||
|
||||
num? get shopId => _shopId;
|
||||
|
||||
num? get shopTableId => _shopTableId;
|
||||
|
||||
num? get status => _status;
|
||||
|
||||
num? get timeoutMinute => _timeoutMinute;
|
||||
|
||||
String? get updateTime => _updateTime;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['arrivedTime'] = _arrivedTime;
|
||||
map['bookingDate'] = _bookingDate;
|
||||
map['bookingPerson'] = _bookingPerson;
|
||||
map['bookingTableNum'] = _bookingTableNum;
|
||||
map['bookingTime'] = _bookingTime;
|
||||
map['bookingType'] = _bookingType;
|
||||
map['createTime'] = _createTime;
|
||||
map['createUserName'] = _createUserName;
|
||||
map['delFlag'] = _delFlag;
|
||||
map['dinerNum'] = _dinerNum;
|
||||
map['diningStandardPrice'] = _diningStandardPrice;
|
||||
map['diningStandardUnit'] = _diningStandardUnit;
|
||||
map['diningType'] = _diningType;
|
||||
map['focus'] = _focus;
|
||||
map['gender'] = _gender;
|
||||
map['id'] = _id;
|
||||
map['orderNo'] = _orderNo;
|
||||
map['phoneNumber'] = _phoneNumber;
|
||||
map['receiveMarketingSms'] = _receiveMarketingSms;
|
||||
map['remark'] = _remark;
|
||||
map['shopId'] = _shopId;
|
||||
map['shopTableId'] = _shopTableId;
|
||||
map['status'] = _status;
|
||||
map['timeoutMinute'] = _timeoutMinute;
|
||||
map['updateTime'] = _updateTime;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
128
lib/data_model/version/update_version_model.dart
Normal file
128
lib/data_model/version/update_version_model.dart
Normal file
@@ -0,0 +1,128 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// createdAt : 1712455187626
|
||||
/// id : 19
|
||||
/// isUp : 0
|
||||
/// message : "需要更新"
|
||||
/// sel : 1
|
||||
/// source : "PC"
|
||||
/// type : "android"
|
||||
/// updatedAt : 1725353572331
|
||||
/// url : "https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/version/1.4.20.exe"
|
||||
/// version : "1.4.21"
|
||||
|
||||
UpdateVersionModel updateVersionModelFromJson(String str) =>
|
||||
UpdateVersionModel.fromJson(json.decode(str));
|
||||
|
||||
String updateVersionModelToJson(UpdateVersionModel data) =>
|
||||
json.encode(data.toJson());
|
||||
|
||||
class UpdateVersionModel {
|
||||
UpdateVersionModel({
|
||||
num? createdAt,
|
||||
num? id,
|
||||
num? isUp,
|
||||
String? message,
|
||||
num? sel,
|
||||
String? source,
|
||||
String? type,
|
||||
num? updatedAt,
|
||||
String? url,
|
||||
String? version,
|
||||
}) {
|
||||
_createdAt = createdAt;
|
||||
_id = id;
|
||||
_isUp = isUp;
|
||||
_message = message;
|
||||
_sel = sel;
|
||||
_source = source;
|
||||
_type = type;
|
||||
_updatedAt = updatedAt;
|
||||
_url = url;
|
||||
_version = version;
|
||||
}
|
||||
|
||||
UpdateVersionModel.fromJson(dynamic json) {
|
||||
_createdAt = json['createdAt'];
|
||||
_id = json['id'];
|
||||
_isUp = json['isUp'];
|
||||
_message = json['message'];
|
||||
_sel = json['sel'];
|
||||
_source = json['source'];
|
||||
_type = json['type'];
|
||||
_updatedAt = json['updatedAt'];
|
||||
_url = json['url'];
|
||||
_version = json['version'];
|
||||
}
|
||||
|
||||
num? _createdAt;
|
||||
num? _id;
|
||||
num? _isUp;
|
||||
String? _message;
|
||||
num? _sel;
|
||||
String? _source;
|
||||
String? _type;
|
||||
num? _updatedAt;
|
||||
String? _url;
|
||||
String? _version;
|
||||
|
||||
UpdateVersionModel copyWith({
|
||||
num? createdAt,
|
||||
num? id,
|
||||
num? isUp,
|
||||
String? message,
|
||||
num? sel,
|
||||
String? source,
|
||||
String? type,
|
||||
num? updatedAt,
|
||||
String? url,
|
||||
String? version,
|
||||
}) =>
|
||||
UpdateVersionModel(
|
||||
createdAt: createdAt ?? _createdAt,
|
||||
id: id ?? _id,
|
||||
isUp: isUp ?? _isUp,
|
||||
message: message ?? _message,
|
||||
sel: sel ?? _sel,
|
||||
source: source ?? _source,
|
||||
type: type ?? _type,
|
||||
updatedAt: updatedAt ?? _updatedAt,
|
||||
url: url ?? _url,
|
||||
version: version ?? _version,
|
||||
);
|
||||
|
||||
num? get createdAt => _createdAt;
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
num? get isUp => _isUp;
|
||||
|
||||
String? get message => _message;
|
||||
|
||||
num? get sel => _sel;
|
||||
|
||||
String? get source => _source;
|
||||
|
||||
String? get type => _type;
|
||||
|
||||
num? get updatedAt => _updatedAt;
|
||||
|
||||
String? get url => _url;
|
||||
|
||||
String? get version => _version;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['createdAt'] = _createdAt;
|
||||
map['id'] = _id;
|
||||
map['isUp'] = _isUp;
|
||||
map['message'] = _message;
|
||||
map['sel'] = _sel;
|
||||
map['source'] = _source;
|
||||
map['type'] = _type;
|
||||
map['updatedAt'] = _updatedAt;
|
||||
map['url'] = _url;
|
||||
map['version'] = _version;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
195
lib/home/home_view.dart
Normal file
195
lib/home/home_view.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
147
lib/home/home_view_model.dart
Normal file
147
lib/home/home_view_model.dart
Normal 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
29
lib/home/order_view.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
3
lib/home/order_view_model.dart
Normal file
3
lib/home/order_view_model.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
import 'package:cashier_reserve/common/base/ui_model.dart';
|
||||
|
||||
class OrderViewModel extends BaseUIModel {}
|
||||
821
lib/home/reserve_left_content_view.dart
Normal file
821
lib/home/reserve_left_content_view.dart
Normal 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),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
98
lib/home/reserve_right_content_view.dart
Normal file
98
lib/home/reserve_right_content_view.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
161
lib/home/reserve_right_table_list.dart
Normal file
161
lib/home/reserve_right_table_list.dart
Normal 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
325
lib/home/reserve_view.dart
Normal 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)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
470
lib/home/reserve_view_model.dart
Normal file
470
lib/home/reserve_view_model.dart
Normal 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] ?? "";
|
||||
}
|
||||
}
|
||||
344
lib/login/login_view.dart
Normal file
344
lib/login/login_view.dart
Normal file
@@ -0,0 +1,344 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cashier_reserve/common/encrypt/pwd.dart';
|
||||
import 'package:cashier_reserve/common/manager/app_manager.dart';
|
||||
import 'package:cashier_reserve/common/print/print.dart';
|
||||
import 'package:cashier_reserve/common/utils/utils.dart';
|
||||
import 'package:cashier_reserve/model/login_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class LoginView extends StatefulWidget {
|
||||
const LoginView({super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _LoginViewState();
|
||||
}
|
||||
}
|
||||
|
||||
class _LoginViewState extends State<LoginView> {
|
||||
int loginType = 0; //0商户 1员工
|
||||
int segmentIndex = 0;
|
||||
|
||||
TextEditingController merchantText = TextEditingController();
|
||||
TextEditingController accountText = TextEditingController();
|
||||
TextEditingController passwordText = TextEditingController();
|
||||
TextEditingController codeText = TextEditingController();
|
||||
|
||||
String authCodeUuid = "";
|
||||
String authCodeImg = "";
|
||||
DateTime? lastPopTime;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
accountText.text = "18049104914";
|
||||
passwordText.text = "czg123456";
|
||||
|
||||
_loadAuthCode();
|
||||
}
|
||||
|
||||
void _loadAuthCode() async {
|
||||
var res = await LoginModel.getLoginAuthCode();
|
||||
|
||||
if (res is Map) {
|
||||
Map<String, dynamic> result = res as Map<String, dynamic>;
|
||||
setState(() {
|
||||
yjPrint("[result]: $result");
|
||||
authCodeUuid = result["uuid"] ?? "";
|
||||
String img = result["code"] ?? "";
|
||||
if (img.startsWith("data:image/png;base64,")) {
|
||||
authCodeImg = img.substring("data:image/png;base64,".length);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
yjPrint("获取验证码失败");
|
||||
}
|
||||
}
|
||||
|
||||
void _goLogin() async {
|
||||
if (loginType == 1) {
|
||||
if (merchantText.text.isEmpty) {
|
||||
Utils.toast("请输入商户号", context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (accountText.text.isEmpty) {
|
||||
Utils.toast("请输入账号", context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (passwordText.text.isEmpty) {
|
||||
Utils.toast("请输入密码", context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (codeText.text.isEmpty) {
|
||||
Utils.toast("请输入验证码", context);
|
||||
return;
|
||||
}
|
||||
|
||||
// String pwd = PwdEncrypt.encrypt(passwordText.text);
|
||||
String pwd = passwordText.text;
|
||||
yjPrint("pwd === $pwd");
|
||||
|
||||
var params = {
|
||||
"username": loginType == 0 ? accountText.text : merchantText.text,
|
||||
"staffUserName": loginType == 1 ? accountText.text : '',
|
||||
"password": pwd,
|
||||
"code": codeText.text,
|
||||
"uuid": authCodeUuid,
|
||||
"loginType": loginType,
|
||||
};
|
||||
|
||||
final r = await LoginModel.login(params);
|
||||
if (r == null) {
|
||||
_loadAuthCode();
|
||||
return;
|
||||
}
|
||||
AppManager.loginSuccess(r);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
merchantText.dispose();
|
||||
accountText.dispose();
|
||||
passwordText.dispose();
|
||||
codeText.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
// 点击返回键的操作
|
||||
if (lastPopTime == null ||
|
||||
DateTime.now().difference(lastPopTime!) >
|
||||
const Duration(seconds: 2)) {
|
||||
lastPopTime = DateTime.now();
|
||||
// Utils.toast('再按一次退出', context);
|
||||
} else {
|
||||
lastPopTime = DateTime.now();
|
||||
// 退出app
|
||||
await SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.black.withOpacity(0.2),
|
||||
body: GestureDetector(
|
||||
onTap: () {
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
},
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
color: const Color(0x55000000),
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(30, 30, 30, 30),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
width: 400,
|
||||
// height: 400,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildTitle(context),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
_buildSegment(context),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
_buildInputTextField(context, merchantText,
|
||||
hintText: "请输入商户号",
|
||||
icon: Icons.store_sharp,
|
||||
isHidden: loginType == 0),
|
||||
_buildInputTextField(context, accountText,
|
||||
hintText: "请输入账号", icon: Icons.people),
|
||||
_buildInputTextField(context, passwordText,
|
||||
hintText: "请输入密码",
|
||||
icon: Icons.lock,
|
||||
isPassword: true),
|
||||
_buildInputTextField(context, codeText,
|
||||
hintText: "请输入验证码",
|
||||
icon: Icons.admin_panel_settings_sharp,
|
||||
isCode: true),
|
||||
_buildLoginBtn(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoginBtn(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
_goLogin();
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 25),
|
||||
height: 35,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
"登录",
|
||||
style: TextStyle(fontSize: 15, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTitle(BuildContext context) {
|
||||
return const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"银收客订餐系统",
|
||||
style: TextStyle(
|
||||
fontSize: 25,
|
||||
color: Color(0xff333333),
|
||||
decoration: TextDecoration.none),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSegment(BuildContext context) {
|
||||
return CupertinoSegmentedControl(
|
||||
//子标签
|
||||
children: {
|
||||
0: Container(
|
||||
width: 60,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
child: const Text(
|
||||
"商户",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
// color: Color(0xff333333),
|
||||
decoration: TextDecoration.none),
|
||||
),
|
||||
),
|
||||
1: Container(
|
||||
width: 60,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
child: const Text(
|
||||
"员工",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
// color: Color(0xff333333),
|
||||
decoration: TextDecoration.none),
|
||||
),
|
||||
),
|
||||
},
|
||||
//当前选中的索引
|
||||
groupValue: segmentIndex,
|
||||
//点击回调
|
||||
onValueChanged: (int index) {
|
||||
setState(() {
|
||||
segmentIndex = index;
|
||||
loginType = index == 0 ? 0 : 1;
|
||||
});
|
||||
},
|
||||
selectedColor: Colors.blue,
|
||||
borderColor: Colors.blue,
|
||||
unselectedColor: Colors.white,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInputTextField(
|
||||
BuildContext context,
|
||||
TextEditingController controller, {
|
||||
String hintText = "",
|
||||
IconData icon = Icons.people,
|
||||
bool isPassword = false,
|
||||
bool isHidden = false,
|
||||
bool isCode = false,
|
||||
}) {
|
||||
if (isHidden) {
|
||||
return Container();
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: const Color(0xffefefef), width: 1),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
|
||||
height: 35,
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
obscureText: isPassword,
|
||||
decoration: InputDecoration(
|
||||
icon: Icon(
|
||||
icon,
|
||||
color: const Color(0xffa6a6a6),
|
||||
),
|
||||
hintText: hintText,
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xff999999),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isCode)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_loadAuthCode();
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(left: 15),
|
||||
width: 100,
|
||||
height: 35,
|
||||
child: (authCodeImg.isNotEmpty)
|
||||
? Image.memory(base64ToUint8List(authCodeImg))
|
||||
: Container(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Uint8List base64ToUint8List(String base64String) {
|
||||
const decoder = Base64Decoder();
|
||||
return decoder.convert(base64String);
|
||||
}
|
||||
}
|
||||
61
lib/main.dart
Normal file
61
lib/main.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cashier_reserve/common/manager/app_manager.dart';
|
||||
import 'package:cashier_reserve/root_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
import 'common/base/widgets.dart';
|
||||
import 'common/local/cupertino_localizations_delegate.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
/// 状态栏沉浸
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: getAppBrightness(false),
|
||||
statusBarBrightness: getAppBrightness(false)));
|
||||
|
||||
await AppManager.initThirdPackage();
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Locale> an = [
|
||||
const Locale('zh', 'CN'),
|
||||
const Locale('en', 'US'),
|
||||
];
|
||||
List<Locale> ios = [
|
||||
const Locale('en', 'US'),
|
||||
const Locale('zh', 'CN'),
|
||||
];
|
||||
|
||||
return MaterialApp(
|
||||
locale: const Locale('zh'),
|
||||
localizationsDelegates: const [
|
||||
CupertinoLocalizationsDelegate(),
|
||||
// 添加Material Design本地化委托
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
// 添加Cupertino本地化委托(用于iOS风格组件的本地化)
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
// 添加通用的Flutter本地化委托
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
title: '订餐系统',
|
||||
supportedLocales: Platform.isIOS ? ios : an,
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
platform: TargetPlatform.iOS,
|
||||
useMaterial3: false,
|
||||
),
|
||||
home: const RootView(),
|
||||
);
|
||||
}
|
||||
}
|
||||
17
lib/model/login_model.dart
Normal file
17
lib/model/login_model.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:cashier_reserve/common/request/request_manager.dart';
|
||||
import 'package:cashier_reserve/data_model/login/login_result.dart';
|
||||
|
||||
/// LoginModel 登录相关请求
|
||||
class LoginModel {
|
||||
|
||||
/// 获取 登录验证码
|
||||
static Future<dynamic> getLoginAuthCode() async {
|
||||
final r = await RequestManager.get("/account/admin/auth/captcha");
|
||||
return r;
|
||||
}
|
||||
|
||||
static Future<LoginResult?> login(Map<String, dynamic> params) async {
|
||||
final r = await RequestManager.post("/account/admin/auth/login", params);
|
||||
return r == null ? null : LoginResult.fromJson(r);
|
||||
}
|
||||
}
|
||||
74
lib/model/reserve_model.dart
Normal file
74
lib/model/reserve_model.dart
Normal file
@@ -0,0 +1,74 @@
|
||||
import 'package:cashier_reserve/common/request/request_manager.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';
|
||||
|
||||
/// ReserveModel 台桌预定相关请求
|
||||
class ReserveModel {
|
||||
/// getShopTableAreaList 获取店铺桌台区域列表
|
||||
static Future<List<TableAreaModel?>> getShopTableAreaList() async {
|
||||
final r = await RequestManager.get("/api/booking/shop-table/area");
|
||||
if (r == null) {
|
||||
return [];
|
||||
}
|
||||
List<TableAreaModel?> list = [];
|
||||
for (var item in r as List) {
|
||||
list.add(TableAreaModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// getAreaTableList 获取区域桌台列表
|
||||
static Future<List<TableModel?>> getAreaTableList(num areaId, String date, String type) async {
|
||||
Map<String, dynamic> paramData = {
|
||||
"bookingDate": date,
|
||||
"bookingType": type,
|
||||
};
|
||||
if (areaId > 0) {
|
||||
paramData["areaId"] = areaId;
|
||||
}
|
||||
final r = await RequestManager.get("/api/booking/shop-table/list", params: paramData);
|
||||
if (r == null) {
|
||||
return [];
|
||||
}
|
||||
List<TableModel?> list = [];
|
||||
for (var item in r as List) {
|
||||
list.add(TableModel.fromJson(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// commitReserveInfo 提交预定信息
|
||||
static Future<void> commitReserveInfo(Map<String, dynamic> params) async {
|
||||
await RequestManager.post("/api/booking/shop-table", params);
|
||||
}
|
||||
|
||||
static Future<Map<String, ReserveLogModel?>> getUserReserveLog(List<String> phoneNos) async {
|
||||
final r = await RequestManager.post("/api/booking/shop-table/summary", {
|
||||
"phoneNos": phoneNos,
|
||||
});
|
||||
|
||||
Map<String, ReserveLogModel?> map = {};
|
||||
if (r == null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
for (var item in r.entries) {
|
||||
map[item.key] = ReserveLogModel.fromJson(item.value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static Future<String> getReserveSms() async {
|
||||
final r = await RequestManager.get("/api/booking/shop-table/sms");
|
||||
return r.toString();
|
||||
}
|
||||
|
||||
static Future<void> updateReserveStatus(num reserveId, num status) async {
|
||||
await RequestManager.post("/api/booking/shop-table/mark-status", {
|
||||
"id": reserveId,
|
||||
"status": status,
|
||||
});
|
||||
}
|
||||
}
|
||||
12
lib/model/version_model.dart
Normal file
12
lib/model/version_model.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:cashier_reserve/common/request/request_manager.dart';
|
||||
import 'package:cashier_reserve/data_model/version/update_version_model.dart';
|
||||
|
||||
class VersionModel {
|
||||
static Future<UpdateVersionModel?> requestNewVersionInfo() async {
|
||||
final r = await RequestManager.get("/api/tbVersion/findBySource", params: {"source": "电话机"});
|
||||
if (r is Map) {
|
||||
return UpdateVersionModel.fromJson(r);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
55
lib/root_view.dart
Normal file
55
lib/root_view.dart
Normal file
@@ -0,0 +1,55 @@
|
||||
import 'package:cashier_reserve/common/manager/app_manager.dart';
|
||||
import 'package:cashier_reserve/home/home_view.dart';
|
||||
import 'package:cashier_reserve/home/home_view_model.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'common/base/provider.dart';
|
||||
import 'common/base/ui.dart';
|
||||
import 'common/utils/utils.dart';
|
||||
|
||||
class RootView extends StatefulWidget {
|
||||
const RootView({super.key});
|
||||
|
||||
@override
|
||||
State<RootView> createState() => _RootView();
|
||||
}
|
||||
|
||||
class _RootView extends State<RootView>
|
||||
with AutomaticKeepAliveClientMixin, RouteAware {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
DateTime? lastPopTime;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
AppManager.setGlobalContext(context);
|
||||
super.build(context);
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
// 点击返回键的操作
|
||||
if (lastPopTime == null ||
|
||||
DateTime.now().difference(lastPopTime!) > const Duration(seconds: 2)) {
|
||||
lastPopTime = DateTime.now();
|
||||
Utils.toast('再按一次退出', context);
|
||||
} else {
|
||||
lastPopTime = DateTime.now();
|
||||
// 退出app
|
||||
await SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
}
|
||||
},
|
||||
child: MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider<HomeViewModel>(
|
||||
create: (_) => HomeViewModel(),
|
||||
)
|
||||
],
|
||||
child: BaseUIController(stateWidget: HomeView()),
|
||||
));
|
||||
}
|
||||
}
|
||||
224
lib/update_version/update_version_view.dart
Normal file
224
lib/update_version/update_version_view.dart
Normal file
@@ -0,0 +1,224 @@
|
||||
import 'package:cashier_reserve/common/print/print.dart';
|
||||
import 'package:cashier_reserve/data_model/version/update_version_model.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_app_installer/flutter_app_installer.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:percent_indicator/percent_indicator.dart';
|
||||
|
||||
class UpdateVersionView extends StatefulWidget {
|
||||
final UpdateVersionModel versionModel;
|
||||
|
||||
const UpdateVersionView({super.key, required this.versionModel});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _UpdateVersionViewState();
|
||||
}
|
||||
}
|
||||
|
||||
class _UpdateVersionViewState extends State<UpdateVersionView> {
|
||||
DateTime? lastPopTime;
|
||||
|
||||
double progress = 0;
|
||||
String progressText = "0%";
|
||||
|
||||
bool isDownload = false;
|
||||
|
||||
// 用于控制下载任务取消的CancelToken
|
||||
CancelToken? _cancelToken;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_cancelToken?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopScope(
|
||||
canPop: (widget.versionModel.isUp ?? 0) != 0,
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
// 点击返回键的操作
|
||||
if (lastPopTime == null ||
|
||||
DateTime.now().difference(lastPopTime!) >
|
||||
const Duration(seconds: 2)) {
|
||||
lastPopTime = DateTime.now();
|
||||
// Utils.toast('再按一次退出', context);
|
||||
} else {
|
||||
lastPopTime = DateTime.now();
|
||||
// 退出app
|
||||
await SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.black.withOpacity(0.2),
|
||||
body: GestureDetector(
|
||||
onTap: () {
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
},
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
color: const Color(0x55000000),
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(30, 30, 30, 30),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
width: 400,
|
||||
// height: 400,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
"发现新版本",
|
||||
style:
|
||||
TextStyle(fontSize: 22, color: Color(0xff333333)),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
widget.versionModel.message ?? "",
|
||||
style: const TextStyle(
|
||||
fontSize: 16, color: Color(0xff666666)),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
isDownload
|
||||
? LinearPercentIndicator(
|
||||
padding: EdgeInsets.zero,
|
||||
animation: false,
|
||||
lineHeight: 20.0,
|
||||
// animationDuration: 2500,
|
||||
percent: progress,
|
||||
center: Text(progressText),
|
||||
// linearStrokeCap: LinearStrokeCap.roundAll,
|
||||
progressColor: Colors.green,
|
||||
)
|
||||
: Container(),
|
||||
isDownload
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
)
|
||||
: Container(),
|
||||
_buildActionViews(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionViews(BuildContext context) {
|
||||
bool isMustUpdate = widget.versionModel.isUp == 1;
|
||||
return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
isMustUpdate
|
||||
? Container()
|
||||
: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Colors.white),
|
||||
foregroundColor:
|
||||
WidgetStateProperty.all(const Color(0xff666666)),
|
||||
minimumSize: WidgetStateProperty.all(const Size(100, 40)),
|
||||
shape: WidgetStateProperty.all(const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
)),
|
||||
),
|
||||
child: const Text("跳过"),
|
||||
),
|
||||
isMustUpdate
|
||||
? Container()
|
||||
: const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
startDownload();
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Colors.blue),
|
||||
foregroundColor: WidgetStateProperty.all(Colors.white),
|
||||
minimumSize:
|
||||
WidgetStateProperty.all(Size(isMustUpdate ? 200 : 100, 40)),
|
||||
shape: WidgetStateProperty.all(const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
)),
|
||||
),
|
||||
child: const Text("立即更新"),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
startDownload() async {
|
||||
setState(() {
|
||||
isDownload = true;
|
||||
});
|
||||
|
||||
String fileUrl = widget.versionModel.url ?? "";
|
||||
if (fileUrl.isEmpty) {
|
||||
yjPrint("下载地址为空");
|
||||
return;
|
||||
}
|
||||
|
||||
_cancelToken?.cancel();
|
||||
|
||||
setState(() {
|
||||
progress = 0.0;
|
||||
progressText = "0.0%";
|
||||
});
|
||||
|
||||
yjPrint("fileUrl: $fileUrl");
|
||||
|
||||
Dio dio = Dio();
|
||||
try {
|
||||
String savePath = await getPhoneLocalPath();
|
||||
String appName = "file.apk";
|
||||
_cancelToken = CancelToken(); // 创建CancelToken对象
|
||||
await dio.download(
|
||||
fileUrl, "$savePath$appName", onReceiveProgress: (received, total) {
|
||||
if (total != -1) {
|
||||
///当前下载的百分比例
|
||||
double currentProgress = received / total;
|
||||
setState(() {
|
||||
progressText = "${(currentProgress * 100).toStringAsFixed(2)}%";
|
||||
progress = double.parse(currentProgress.toStringAsFixed(2));
|
||||
});
|
||||
yjPrint(progressText);
|
||||
}
|
||||
}, cancelToken: _cancelToken);
|
||||
|
||||
yjPrint("文件下载成功");
|
||||
_cancelToken = null;
|
||||
installApk("$savePath$appName");
|
||||
} catch (e) {
|
||||
yjPrint("文件下载失败:$e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> getPhoneLocalPath() async {
|
||||
final directory = await getExternalStorageDirectory();
|
||||
return directory?.path ?? '';
|
||||
}
|
||||
|
||||
installApk(String apkPath) async {
|
||||
final FlutterAppInstaller flutterAppInstaller = FlutterAppInstaller();
|
||||
flutterAppInstaller.installApk(
|
||||
filePath: apkPath,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user