站点增加广播
This commit is contained in:
@@ -28,6 +28,11 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
? DateFormat('yyyy-MM-dd HH:mm').format(customEndTime!)
|
? DateFormat('yyyy-MM-dd HH:mm').format(customEndTime!)
|
||||||
: '点击选择结束时间';
|
: '点击选择结束时间';
|
||||||
|
|
||||||
|
// --- 站点广播相关 ---
|
||||||
|
final TextEditingController broadcastTitleController = TextEditingController();
|
||||||
|
final TextEditingController broadcastContentController = TextEditingController();
|
||||||
|
final RxInt selectedTabIndex = 0.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
@@ -120,8 +125,6 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
DateTime initialDate = isStart ? (customStartTime ?? now) : (customEndTime ?? now);
|
DateTime initialDate = isStart ? (customStartTime ?? now) : (customEndTime ?? now);
|
||||||
|
|
||||||
// 确保初始时间不早于最小允许时间
|
|
||||||
// 将 minimumDate 稍微提前一点点(比如1分钟)
|
|
||||||
DateTime minLimit = isStart
|
DateTime minLimit = isStart
|
||||||
? now.subtract(const Duration(minutes: 1))
|
? now.subtract(const Duration(minutes: 1))
|
||||||
: (customStartTime ?? now).subtract(const Duration(minutes: 1));
|
: (customStartTime ?? now).subtract(const Duration(minutes: 1));
|
||||||
@@ -157,7 +160,6 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (isStart) {
|
if (isStart) {
|
||||||
customStartTime = tempDate;
|
customStartTime = tempDate;
|
||||||
// 如果开始时间变动后,结束时间早于开始时间,自动顺延一天
|
|
||||||
if (customEndTime != null &&
|
if (customEndTime != null &&
|
||||||
customEndTime!.isBefore(customStartTime!)) {
|
customEndTime!.isBefore(customStartTime!)) {
|
||||||
customEndTime = customStartTime!.add(const Duration(days: 1));
|
customEndTime = customStartTime!.add(const Duration(days: 1));
|
||||||
@@ -250,8 +252,54 @@ class ReservationController extends GetxController with BaseControllerMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 发送站点广播
|
||||||
|
void sendBroadcast() async {
|
||||||
|
String title = broadcastTitleController.text.trim();
|
||||||
|
String content = broadcastContentController.text.trim();
|
||||||
|
|
||||||
|
if (title.isEmpty) {
|
||||||
|
showToast("请输入通知标题");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (content.isEmpty) {
|
||||||
|
showToast("请输入通知内容");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showLoading("发送中...");
|
||||||
|
try {
|
||||||
|
var responseData = await HttpService.to.post(
|
||||||
|
'appointment/notice/push/station/broadcast',
|
||||||
|
data: {
|
||||||
|
'title': title,
|
||||||
|
'content': content,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
dismissLoading();
|
||||||
|
if (responseData != null) {
|
||||||
|
var result = BaseModel.fromJson(responseData.data);
|
||||||
|
if (result.code == 0) {
|
||||||
|
showSuccessToast("广播发送成功");
|
||||||
|
} else {
|
||||||
|
showErrorToast(result.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
dismissLoading();
|
||||||
|
showToast("发送失败,请稍后重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void logout() async {
|
void logout() async {
|
||||||
await StorageService.to.clearLoginInfo();
|
await StorageService.to.clearLoginInfo();
|
||||||
Get.offAll(() => LoginPage());
|
Get.offAll(() => LoginPage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
broadcastTitleController.dispose();
|
||||||
|
broadcastContentController.dispose();
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
leading: const Icon(Icons.local_gas_station, color: Colors.blue, size: 40),
|
leading: const Icon(Icons.local_gas_station, color: Colors.blue, size: 40),
|
||||||
title: Text(
|
title: Text(
|
||||||
controller.name,
|
controller.name,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
|
||||||
),
|
),
|
||||||
subtitle: Text(controller.address),
|
subtitle: Text(controller.address),
|
||||||
trailing: Container(
|
trailing: Container(
|
||||||
@@ -57,7 +57,7 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
controller.selectedOperationStatus,
|
controller.selectedOperationStatus,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
@@ -100,85 +100,204 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 构建包含所有信息表单的卡片
|
/// 构建包含所有信息表单的卡片(增加 Tab 切换功能)
|
||||||
Widget _buildInfoFormCard(BuildContext context) {
|
Widget _buildInfoFormCard(BuildContext context) {
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
child: Padding(
|
clipBehavior: Clip.antiAlias, // 确保 Tab 背景圆角生效
|
||||||
padding: const EdgeInsets.all(16.0),
|
child: Column(
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
// Tab 切换栏
|
||||||
children: [
|
Obx(() => Container(
|
||||||
// --- 基本信息 ---
|
color: Colors.grey[50],
|
||||||
_buildSectionTitle('基本信息'),
|
child: Row(
|
||||||
_buildDisplayField(label: '站点名称', value: controller.name),
|
children: [
|
||||||
_buildDisplayField(label: '运营企业', value: controller.operatingEnterprise),
|
_buildTabItem(0, Icons.business_outlined, '站点信息'),
|
||||||
_buildDisplayField(label: '站点地址', value: controller.address),
|
_buildTabItem(1, Icons.campaign_outlined, '站点广播'),
|
||||||
const SizedBox(height: 16),
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
const Divider(height: 1),
|
||||||
|
// 内容区域
|
||||||
|
Obx(() => controller.selectedTabIndex.value == 0
|
||||||
|
? _buildStationInfo(context)
|
||||||
|
: _buildStationBroadcast(context)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// --- 价格信息 ---
|
/// 构建单个 Tab 项
|
||||||
_buildSectionTitle('价格信息'),
|
Widget _buildTabItem(int index, IconData icon, String label) {
|
||||||
// _buildDisplayField(label: '氢气价格 (元/kg)', value: controller.costPrice),
|
bool isSelected = controller.selectedTabIndex.value == index;
|
||||||
_buildDisplayField(label: '官方价格 (元/kg)', value: controller.customerPrice),
|
return Expanded(
|
||||||
const SizedBox(height: 16),
|
child: InkWell(
|
||||||
|
onTap: () => controller.selectedTabIndex.value = index,
|
||||||
// --- 运营信息 ---
|
child: Container(
|
||||||
_buildSectionTitle('运营信息'),
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||||
Text('运营状态', style: TextStyle(color: Colors.grey[600], fontSize: 14)),
|
decoration: BoxDecoration(
|
||||||
const SizedBox(height: 8),
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
// 下拉选择框
|
color: isSelected ? Colors.blue : Colors.transparent,
|
||||||
DropdownButtonFormField<String>(
|
width: 2,
|
||||||
value: controller.selectedOperationStatus,
|
|
||||||
items: controller.operationStatusOptions.map((String value) {
|
|
||||||
return DropdownMenuItem<String>(value: value, child: Text(value));
|
|
||||||
}).toList(),
|
|
||||||
onChanged: controller.onOperationStatusChanged,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
),
|
||||||
|
child: Row(
|
||||||
// 根据状态动态显示 UI
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
if (controller.selectedOperationStatus == "营运中")
|
children: [
|
||||||
_buildDisplayField(label: '营业时间', value: controller.timeStr)
|
Icon(icon, size: 20, color: isSelected ? Colors.blue : Colors.grey[600]),
|
||||||
else
|
const SizedBox(width: 8),
|
||||||
Column(
|
Text(
|
||||||
children: [
|
label,
|
||||||
_buildClickField(
|
style: TextStyle(
|
||||||
label: '开始时间',
|
fontSize: 15,
|
||||||
value: controller.customStartTimeStr,
|
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||||
onTap: () => controller.pickDateTime(context, true),
|
color: isSelected ? Colors.blue : Colors.grey[600],
|
||||||
),
|
),
|
||||||
_buildClickField(
|
|
||||||
label: '结束时间',
|
|
||||||
value: controller.customEndTimeStr,
|
|
||||||
onTap: () => controller.pickDateTime(context, false),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
],
|
||||||
_buildDisplayField(label: '联系电话', value: controller.phone),
|
),
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
//保存按钮
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: controller.saveInfo,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
minimumSize: const Size(double.infinity, 48),
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
|
||||||
),
|
|
||||||
child: const Text('保存信息', style: TextStyle(fontSize: 16)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 站点信息子视图
|
||||||
|
Widget _buildStationInfo(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildSectionTitle('基本信息'),
|
||||||
|
_buildDisplayField(label: '站点名称', value: controller.name),
|
||||||
|
_buildDisplayField(label: '运营企业', value: controller.operatingEnterprise),
|
||||||
|
_buildDisplayField(label: '站点地址', value: controller.address),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_buildSectionTitle('价格信息'),
|
||||||
|
_buildDisplayField(label: '官方价格 (元/kg)', value: controller.customerPrice),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_buildSectionTitle('运营信息'),
|
||||||
|
Text('运营状态', style: TextStyle(color: Colors.grey[600], fontSize: 14)),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
DropdownButtonFormField<String>(
|
||||||
|
value: controller.selectedOperationStatus,
|
||||||
|
items: controller.operationStatusOptions.map((String value) {
|
||||||
|
return DropdownMenuItem<String>(value: value, child: Text(value));
|
||||||
|
}).toList(),
|
||||||
|
onChanged: controller.onOperationStatusChanged,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
if (controller.selectedOperationStatus == "营运中")
|
||||||
|
_buildDisplayField(label: '营业时间', value: controller.timeStr)
|
||||||
|
else
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
_buildClickField(
|
||||||
|
label: '开始时间',
|
||||||
|
value: controller.customStartTimeStr,
|
||||||
|
onTap: () => controller.pickDateTime(context, true),
|
||||||
|
),
|
||||||
|
_buildClickField(
|
||||||
|
label: '结束时间',
|
||||||
|
value: controller.customEndTimeStr,
|
||||||
|
onTap: () => controller.pickDateTime(context, false),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
_buildDisplayField(label: '联系电话', value: controller.phone),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: controller.saveInfo,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
minimumSize: const Size(double.infinity, 48),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
),
|
||||||
|
child: const Text('保存信息', style: TextStyle(fontSize: 16)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 站点广播子视图
|
||||||
|
Widget _buildStationBroadcast(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.campaign, color: Colors.blue, size: 28),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
const Text(
|
||||||
|
'站点广播通知',
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 13),
|
||||||
|
_buildTextFieldLabel('通知标题'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
SizedBox(
|
||||||
|
height: 45.h,
|
||||||
|
child: TextField(
|
||||||
|
controller: controller.broadcastTitleController,
|
||||||
|
maxLength: 30,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: '例如:临时闭站通知',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey[400], fontSize: 14),
|
||||||
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
|
counterText: '', // 隐藏原生计数器,我们可以按需自定义
|
||||||
|
),
|
||||||
|
),),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildTextFieldLabel('通知内容'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: controller.broadcastContentController,
|
||||||
|
maxLength: 150,
|
||||||
|
maxLines: 5,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: '请输入通知内容...',
|
||||||
|
hintStyle: TextStyle(color: Colors.grey[400], fontSize: 14),
|
||||||
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: controller.sendBroadcast,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
minimumSize: const Size(double.infinity, 50),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
child: const Text('发送', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTextFieldLabel(String label) {
|
||||||
|
return Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black87),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// 构建带标题的表单区域
|
/// 构建带标题的表单区域
|
||||||
Widget _buildSectionTitle(String title) {
|
Widget _buildSectionTitle(String title) {
|
||||||
return Padding(
|
return Padding(
|
||||||
@@ -273,7 +392,7 @@ class ReservationPage extends GetView<ReservationController> {
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.verified_outlined, color: Colors.blue, size: 20),
|
const Icon(Icons.verified_outlined, color: Colors.blue, size: 20),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: FutureBuilder<String>(
|
child: FutureBuilder<String>(
|
||||||
|
|||||||
Reference in New Issue
Block a user