帮助与反馈页面开发
This commit is contained in:
716
lib/features/v2/my/presentation/pages/help_feedback_page.dart
Normal file
716
lib/features/v2/my/presentation/pages/help_feedback_page.dart
Normal file
@@ -0,0 +1,716 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:maibu_satabot_v2/core/localization/app_localizations.dart';
|
||||
import 'package:maibu_satabot_v2/core/theme/AppTheme.dart';
|
||||
|
||||
/// 帮助与反馈页面
|
||||
class HelpFeedbackPage extends StatefulWidget {
|
||||
const HelpFeedbackPage({super.key});
|
||||
|
||||
@override
|
||||
State<HelpFeedbackPage> createState() => _HelpFeedbackPageState();
|
||||
}
|
||||
|
||||
class _HelpFeedbackPageState extends State<HelpFeedbackPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
final TextEditingController _feedbackController = TextEditingController();
|
||||
final TextEditingController _contactController = TextEditingController();
|
||||
int _selectedFeedbackType = 0;
|
||||
List<String> _selectedImages = [];
|
||||
|
||||
static const _feedbackTypes = [
|
||||
{'icon': Icons.lightbulb_outline, 'label': '功能建议'},
|
||||
{'icon': Icons.bug_report_outlined, 'label': 'Bug报告'},
|
||||
{'icon': Icons.sentiment_dissatisfied_outlined, 'label': '体验问题'},
|
||||
{'icon': Icons.more_horiz, 'label': '其他'},
|
||||
];
|
||||
|
||||
static const _faqList = [
|
||||
{
|
||||
'category': '设备绑定',
|
||||
'questions': [
|
||||
{
|
||||
'q': '扫码绑定失败怎么办?',
|
||||
'a': '请检查设备是否已开机并处于待绑定状态,确保手机蓝牙已开启且网络正常。如仍无法绑定,请尝试重启设备后重新扫码。'
|
||||
},
|
||||
{
|
||||
'q': '搜不到设备蓝牙信号?',
|
||||
'a': '请确认设备已开机且蓝牙指示灯正常闪烁。尝试靠近设备(1米以内)后重新搜索。如多个设备同时绑定,请逐一操作。'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'category': '路径规划',
|
||||
'questions': [
|
||||
{
|
||||
'q': '无法生成路径?',
|
||||
'a': '请确保已绘制完整的作业区域(至少3个打点),并检查GPS信号是否良好。如果使用自动规划模式,请确认区域边界没有自相交。'
|
||||
},
|
||||
{
|
||||
'q': '地图不显示或定位偏移?',
|
||||
'a': '请检查手机GPS是否已开启,并授权App位置权限。在室内或信号较弱的区域可能出现定位偏移,建议到开阔区域使用。'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'category': '远程控制',
|
||||
'questions': [
|
||||
{
|
||||
'q': '远程视频卡顿/连接超时?',
|
||||
'a': '请检查手机和设备的网络连接是否稳定。建议在4G/Wi-Fi信号良好的环境下使用远程控制功能。如持续卡顿,可尝试降低视频分辨率。'
|
||||
},
|
||||
{
|
||||
'q': '急停功能怎么使用?',
|
||||
'a': '在远程控制页面,点击屏幕中央红色急停按钮即可立即停止设备运行。急停后需手动解除才能恢复操作。'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'category': '工单系统',
|
||||
'questions': [
|
||||
{
|
||||
'q': '如何提交工单?',
|
||||
'a': '在「工单」页面点击右下角「新建工单」按钮,选择工单类型,填写问题描述并上传相关图片后提交即可。'
|
||||
},
|
||||
{
|
||||
'q': '工单状态怎么看?',
|
||||
'a': '在「工单」页面可查看所有已提交的工单及其当前状态(待处理/处理中/已完成)。点击具体工单可查看详情和回复。'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
'category': '账号问题',
|
||||
'questions': [
|
||||
{
|
||||
'q': '忘记密码怎么办?',
|
||||
'a': '在登录页面点击「忘记密码」,通过注册手机号验证后即可重置密码。'
|
||||
},
|
||||
{
|
||||
'q': '换设备登录需要重新绑定?',
|
||||
'a': '更换手机登录同一账号后,设备绑定关系不会丢失。但需要在新设备上重新搜索和连接设备。'
|
||||
},
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: 2, vsync: this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
_feedbackController.dispose();
|
||||
_contactController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: const SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: context.appColors.pageBackground,
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back, color: context.appColors.textPrimary),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
title: Text(
|
||||
AppLocalizations.of(context).translate('my_v2.help_feedback'),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.appColors.textPrimary,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
bottom: TabBar(
|
||||
controller: _tabController,
|
||||
labelColor: const Color(0xFF165DFF),
|
||||
unselectedLabelColor: context.appColors.textTertiary,
|
||||
indicatorColor: const Color(0xFF165DFF),
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
tabs: const [
|
||||
Tab(text: '常见问题'),
|
||||
Tab(text: '意见反馈'),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [
|
||||
_buildFAQTab(),
|
||||
_buildFeedbackTab(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 常见问题 ====================
|
||||
|
||||
Widget _buildFAQTab() {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
..._faqList.map((category) => _buildFAQCategory(category)),
|
||||
const SizedBox(height: 20),
|
||||
_buildContactSection(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFAQCategory(Map<String, dynamic> categoryData) {
|
||||
final questions = categoryData['questions'] as List;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8, bottom: 8),
|
||||
child: Text(
|
||||
categoryData['category'] as String,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.appColors.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.appColors.cardBackground,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.appColors.cardShadow,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: questions.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final item = entry.value as Map<String, dynamic>;
|
||||
final isLast = index == questions.length - 1;
|
||||
return _buildFAQItem(
|
||||
item['q'] as String,
|
||||
item['a'] as String,
|
||||
isLast,
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFAQItem(String question, String answer, bool isLast) {
|
||||
return Column(
|
||||
children: [
|
||||
ExpansionTile(
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
shape: const Border(),
|
||||
collapsedShape: const Border(),
|
||||
title: Text(
|
||||
question,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: context.appColors.textPrimary,
|
||||
),
|
||||
),
|
||||
children: [
|
||||
Text(
|
||||
answer,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
height: 1.6,
|
||||
color: context.appColors.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!isLast)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 48),
|
||||
child: Divider(
|
||||
height: 1,
|
||||
thickness: 0.5,
|
||||
color: context.appColors.divider,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 联系客服 ====================
|
||||
|
||||
Widget _buildContactSection() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: context.appColors.cardBackground,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.appColors.cardShadow,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'联系客服',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.appColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildContactItem(
|
||||
Icons.phone_outlined,
|
||||
'客服热线',
|
||||
'400-000-0000',
|
||||
onTap: () => _showPhoneDialog('400-000-0000'),
|
||||
),
|
||||
const Divider(height: 24),
|
||||
_buildContactItem(
|
||||
Icons.qr_code_2,
|
||||
'企业微信',
|
||||
'扫码添加客服微信',
|
||||
onTap: () => debugPrint('显示企业微信二维码'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContactItem(
|
||||
IconData icon,
|
||||
String title,
|
||||
String subtitle, {
|
||||
VoidCallback? onTap,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, size: 22, color: context.appColors.textSecondary),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: context.appColors.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: context.appColors.textTertiary,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: context.appColors.textTertiary,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 意见反馈 ====================
|
||||
|
||||
Widget _buildFeedbackTab() {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
// 反馈类型
|
||||
_buildSectionCard(
|
||||
title: '反馈类型',
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: _feedbackTypes.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final type = entry.value;
|
||||
final isSelected = _selectedFeedbackType == index;
|
||||
return GestureDetector(
|
||||
onTap: () => setState(() => _selectedFeedbackType = index),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 14,
|
||||
vertical: 8,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? const Color(0xFF165DFF).withOpacity(0.1)
|
||||
: context.appColors.fillBackground,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? const Color(0xFF165DFF)
|
||||
: Colors.transparent,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
type['icon'] as IconData,
|
||||
size: 16,
|
||||
color: isSelected
|
||||
? const Color(0xFF165DFF)
|
||||
: context.appColors.textSecondary,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
type['label'] as String,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isSelected
|
||||
? const Color(0xFF165DFF)
|
||||
: context.appColors.textSecondary,
|
||||
fontWeight:
|
||||
isSelected ? FontWeight.w600 : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 问题描述
|
||||
_buildSectionCard(
|
||||
title: '问题描述',
|
||||
child: TextField(
|
||||
controller: _feedbackController,
|
||||
maxLines: 5,
|
||||
maxLength: 500,
|
||||
decoration: InputDecoration(
|
||||
hintText: '请详细描述您遇到的问题或建议...',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
color: context.appColors.textTertiary,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: context.appColors.divider),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: context.appColors.divider),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: const BorderSide(
|
||||
color: Color(0xFF165DFF),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: context.appColors.fillBackground,
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 添加截图
|
||||
_buildSectionCard(
|
||||
title: '添加截图 (${_selectedImages.length}/6)',
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
..._selectedImages.asMap().entries.map((entry) {
|
||||
return _buildImageItem(entry.key, entry.value);
|
||||
}),
|
||||
if (_selectedImages.length < 6) _buildAddImageButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 联系方式
|
||||
_buildSectionCard(
|
||||
title: '联系方式(选填)',
|
||||
child: TextField(
|
||||
controller: _contactController,
|
||||
decoration: InputDecoration(
|
||||
hintText: '手机号/微信号,方便我们回访',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
color: context.appColors.textTertiary,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: context.appColors.divider),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: context.appColors.divider),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: const BorderSide(
|
||||
color: Color(0xFF165DFF),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: context.appColors.fillBackground,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 14,
|
||||
),
|
||||
prefixIcon: const Icon(
|
||||
Icons.phone_outlined,
|
||||
size: 20,
|
||||
color: Color(0xFF86909C),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// 提交按钮
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 48,
|
||||
child: ElevatedButton(
|
||||
onPressed: _submitFeedback,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF165DFF),
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'提交反馈',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionCard({
|
||||
required String title,
|
||||
required Widget child,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: context.appColors.cardBackground,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.appColors.cardShadow,
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.appColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
child,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImageItem(int index, String path) {
|
||||
return Stack(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Image.file(
|
||||
File(path),
|
||||
width: 80,
|
||||
height: 80,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) => Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
color: context.appColors.fillBackground,
|
||||
child: Icon(
|
||||
Icons.broken_image_outlined,
|
||||
color: context.appColors.textTertiary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 2,
|
||||
right: 2,
|
||||
child: GestureDetector(
|
||||
onTap: () => setState(() => _selectedImages.removeAt(index)),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.black54,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 14,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAddImageButton() {
|
||||
return GestureDetector(
|
||||
onTap: _pickImage,
|
||||
child: Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: context.appColors.fillBackground,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: context.appColors.divider,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add_photo_alternate_outlined,
|
||||
size: 28,
|
||||
color: context.appColors.textTertiary,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'添加截图',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: context.appColors.textTertiary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 交互方法 ====================
|
||||
|
||||
void _showPhoneDialog(String phoneNumber) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('客服热线'),
|
||||
content: SelectableText(
|
||||
phoneNumber,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('关闭'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: phoneNumber));
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(this.context).showSnackBar(
|
||||
const SnackBar(content: Text('号码已复制')),
|
||||
);
|
||||
},
|
||||
child: const Text('复制号码'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _pickImage() {
|
||||
// 占位:接入 image_picker 后实现
|
||||
debugPrint('选择图片');
|
||||
setState(() {
|
||||
_selectedImages.add('placeholder_${_selectedImages.length}');
|
||||
});
|
||||
}
|
||||
|
||||
void _submitFeedback() {
|
||||
if (_feedbackController.text.trim().isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('请输入问题描述'),
|
||||
backgroundColor: Color(0xFFF53F3F),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: 调用后端接口提交反馈
|
||||
debugPrint('提交反馈: type=${_feedbackTypes[_selectedFeedbackType]['label']}, '
|
||||
'content=${_feedbackController.text}, '
|
||||
'images=${_selectedImages.length}, '
|
||||
'contact=${_contactController.text}');
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: const Text('反馈已提交,感谢您的支持!'),
|
||||
backgroundColor: context.appColors.success,
|
||||
),
|
||||
);
|
||||
|
||||
// 重置表单
|
||||
_feedbackController.clear();
|
||||
_contactController.clear();
|
||||
setState(() {
|
||||
_selectedFeedbackType = 0;
|
||||
_selectedImages.clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import 'package:maibu_satabot_v2/features/v2/my/presentation/widgets/quick_actio
|
||||
import 'package:maibu_satabot_v2/features/v2/my/presentation/widgets/menu_item_card.dart';
|
||||
import 'package:maibu_satabot_v2/features/v2/my/presentation/pages/system_settings_page.dart';
|
||||
import 'package:maibu_satabot_v2/features/v2/my/presentation/pages/profile_detail_page.dart';
|
||||
import 'package:maibu_satabot_v2/features/v2/my/presentation/pages/help_feedback_page.dart';
|
||||
|
||||
/// 我的页面(100% 还原设计稿)
|
||||
class MyPage extends StatelessWidget {
|
||||
@@ -75,90 +76,91 @@ class _MyPageContent extends StatelessWidget {
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
// 用户信息卡片(延伸到状态栏)
|
||||
UserProfileCard(
|
||||
name: userName,
|
||||
role: userRole,
|
||||
avatar: user?.avatar,
|
||||
onTap: () {
|
||||
// 跳转到个人信息详情页
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ProfileDetailPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
// 快捷入口卡片(负margin向上重叠)
|
||||
Transform.translate(
|
||||
offset: const Offset(0, -15),
|
||||
child: QuickActionCard(
|
||||
onMessageTap: () {
|
||||
debugPrint('我的消息');
|
||||
},
|
||||
onFavoriteTap: () {
|
||||
debugPrint('我的收藏');
|
||||
},
|
||||
onWorkOrderTap: () {
|
||||
debugPrint('我的工单');
|
||||
},
|
||||
),
|
||||
),
|
||||
// 菜单列表(白色卡片)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(
|
||||
top: 15,
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: context.appColors.cardBackground,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.appColors.cardShadow,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.zero,
|
||||
// 末尾追加退出登录,占据“关于我们”原来的位置
|
||||
itemCount: state.menuItems.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
// 退出登录(列表最后一项,在可滚动区域内避免底部溢出)
|
||||
if (index == state.menuItems.length) {
|
||||
return _buildLogoutButton(context);
|
||||
}
|
||||
final item = state.menuItems[index];
|
||||
// 🔥 夜间模式开关值以全局 ThemeCubit 状态为准
|
||||
final displayItem = item.id == 'night_mode'
|
||||
? item.copyWith(switchValue: isDarkMode)
|
||||
: item;
|
||||
return MenuItemCard(
|
||||
item: displayItem,
|
||||
onTap: () => _handleMenuItemTap(context, cubit, item),
|
||||
onSwitchChanged: item.hasSwitch
|
||||
? (value) {
|
||||
// 更新菜单 UI 状态
|
||||
cubit.toggleNightMode(value);
|
||||
// 🔥 真正切换全局主题
|
||||
context
|
||||
.read<ThemeCubit>()
|
||||
.toggleDarkMode(value);
|
||||
}
|
||||
: null,
|
||||
children: [
|
||||
// 用户信息卡片(延伸到状态栏)
|
||||
UserProfileCard(
|
||||
name: userName,
|
||||
role: userRole,
|
||||
avatar: user?.avatar,
|
||||
onTap: () {
|
||||
// 跳转到个人信息详情页
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ProfileDetailPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
// 快捷入口卡片(负margin向上重叠)
|
||||
Transform.translate(
|
||||
offset: const Offset(0, -15),
|
||||
child: QuickActionCard(
|
||||
onMessageTap: () {
|
||||
debugPrint('我的消息');
|
||||
},
|
||||
onFavoriteTap: () {
|
||||
debugPrint('我的收藏');
|
||||
},
|
||||
onWorkOrderTap: () {
|
||||
debugPrint('我的工单');
|
||||
},
|
||||
),
|
||||
),
|
||||
// 菜单列表(白色卡片)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(
|
||||
top: 15,
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: context.appColors.cardBackground,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.appColors.cardShadow,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.zero,
|
||||
// 末尾追加退出登录,占据“关于我们”原来的位置
|
||||
itemCount: state.menuItems.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
// 退出登录(列表最后一项,在可滚动区域内避免底部溢出)
|
||||
if (index == state.menuItems.length) {
|
||||
return _buildLogoutButton(context);
|
||||
}
|
||||
final item = state.menuItems[index];
|
||||
// 🔥 夜间模式开关值以全局 ThemeCubit 状态为准
|
||||
final displayItem = item.id == 'night_mode'
|
||||
? item.copyWith(switchValue: isDarkMode)
|
||||
: item;
|
||||
return MenuItemCard(
|
||||
item: displayItem,
|
||||
onTap: () =>
|
||||
_handleMenuItemTap(context, cubit, item),
|
||||
onSwitchChanged: item.hasSwitch
|
||||
? (value) {
|
||||
// 更新菜单 UI 状态
|
||||
cubit.toggleNightMode(value);
|
||||
// 🔥 真正切换全局主题
|
||||
context.read<ThemeCubit>().toggleDarkMode(
|
||||
value,
|
||||
);
|
||||
}
|
||||
: null,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -196,6 +198,13 @@ class _MyPageContent extends StatelessWidget {
|
||||
MaterialPageRoute(builder: (context) => const SystemSettingsPage()),
|
||||
);
|
||||
break;
|
||||
case 'help_feedback':
|
||||
// 跳转到帮助与反馈页面
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const HelpFeedbackPage()),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
debugPrint('点击菜单: ${item.title}');
|
||||
// TODO: 根据 ID 跳转到对应页面
|
||||
|
||||
Reference in New Issue
Block a user