新增 mobile/:畅联手机端 Flutter 工程(B-58)
- 登录:工号+密码走 account-service /api/login,自动登录、被踢下线回登录页 - 消息:会话列表(未读角标/免打扰/时间)、单聊群聊、文字/语音/图片/文件消息、失败重发、历史分页 - 通讯录:新的同事/我的群聊入口、按部门分组(好友 ex 字段)、搜索 - 通话:一对一语音通话(信令走 OpenIM 自定义消息 + LiveKit,token 走 /api/rtc_token) - 界面按确认效果图 m1/m2/m3 实现,主色 #3B87F5,四态视图齐全,无英文残留
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
|
||||
|
||||
import '../services/im_service.dart';
|
||||
import '../theme.dart';
|
||||
import '../widgets/avatar.dart';
|
||||
import 'login_screen.dart';
|
||||
|
||||
/// 「我的」页:大头像 + 姓名 + 工号,设置 / 关于 / 退出登录
|
||||
class MineScreen extends StatefulWidget {
|
||||
const MineScreen({super.key});
|
||||
|
||||
@override
|
||||
State<MineScreen> createState() => _MineScreenState();
|
||||
}
|
||||
|
||||
class _MineScreenState extends State<MineScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
IMService.instance.refreshSelfInfo();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('我的')),
|
||||
body: AnimatedBuilder(
|
||||
animation: IMService.instance,
|
||||
builder: (context, _) {
|
||||
final user = IMService.instance.selfInfo;
|
||||
final nickname = user?.nickname?.isNotEmpty == true ? user!.nickname! : '';
|
||||
final userID = IMService.instance.currentUserID ?? '';
|
||||
return ListView(
|
||||
children: [
|
||||
// 头部:大头像 + 姓名 + 工号
|
||||
Container(
|
||||
color: AppColors.pageBg,
|
||||
padding: const EdgeInsets.all(AppGap.x4),
|
||||
child: Row(
|
||||
children: [
|
||||
NameAvatar(name: nickname.isNotEmpty ? nickname : userID, faceURL: user?.faceURL, size: 64),
|
||||
const SizedBox(width: AppGap.x4),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
nickname.isNotEmpty ? nickname : userID,
|
||||
style: const TextStyle(fontSize: AppFont.title, fontWeight: FontWeight.w600, color: AppColors.textPrimary),
|
||||
),
|
||||
const SizedBox(height: AppGap.x1),
|
||||
Text(
|
||||
'工号:$userID',
|
||||
style: const TextStyle(fontSize: AppFont.sub, color: AppColors.textSecondary),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppGap.x3),
|
||||
_item(Icons.settings_outlined, '设置', () => _showSettings(nickname)),
|
||||
const Divider(indent: AppGap.x4),
|
||||
_item(Icons.info_outline, '关于', _showAbout),
|
||||
const Divider(indent: AppGap.x4),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.logout, color: AppColors.danger),
|
||||
title: const Text('退出登录', style: TextStyle(fontSize: AppFont.body, color: AppColors.danger)),
|
||||
onTap: _confirmLogout,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _item(IconData icon, String label, VoidCallback onTap) {
|
||||
return ListTile(
|
||||
leading: Icon(icon, color: AppColors.textPrimary),
|
||||
title: Text(label, style: const TextStyle(fontSize: AppFont.body)),
|
||||
trailing: const Icon(Icons.chevron_right, color: AppColors.textSecondary),
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
|
||||
/// 设置:目前只有修改昵称
|
||||
void _showSettings(String currentNickname) {
|
||||
final ctrl = TextEditingController(text: currentNickname);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (ctx) => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: AppGap.x4,
|
||||
right: AppGap.x4,
|
||||
top: AppGap.x4,
|
||||
bottom: MediaQuery.of(ctx).viewInsets.bottom + AppGap.x4,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Text('修改昵称', style: TextStyle(fontSize: AppFont.title, fontWeight: FontWeight.w600)),
|
||||
const SizedBox(height: AppGap.x4),
|
||||
TextField(
|
||||
controller: ctrl,
|
||||
decoration: InputDecoration(
|
||||
hintText: '请输入新昵称',
|
||||
filled: true,
|
||||
fillColor: AppColors.searchBg,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppGap.x4),
|
||||
FilledButton(
|
||||
onPressed: () async {
|
||||
final name = ctrl.text.trim();
|
||||
if (name.isEmpty) return;
|
||||
try {
|
||||
await OpenIM.iMManager.userManager.setSelfInfo(nickname: name);
|
||||
await IMService.instance.refreshSelfInfo();
|
||||
if (ctx.mounted) Navigator.of(ctx).pop();
|
||||
} catch (_) {
|
||||
if (ctx.mounted) {
|
||||
ScaffoldMessenger.of(ctx).showSnackBar(
|
||||
const SnackBar(content: Text('修改失败,请检查网络后重试'), duration: Duration(seconds: 2)),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
style: FilledButton.styleFrom(backgroundColor: AppColors.primary),
|
||||
child: const Text('保存', style: TextStyle(fontSize: AppFont.body)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showAbout() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('关于畅联'),
|
||||
content: const Text('畅联 1.0.0\n公司内部通讯工具,供同事之间消息、文件与语音通话使用。'),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.of(ctx).pop(), child: const Text('知道了')),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _confirmLogout() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('退出登录'),
|
||||
content: const Text('退出后要重新输入工号和密码,确定退出吗?'),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.of(ctx).pop(), child: const Text('取消')),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(ctx).pop();
|
||||
await LoginScreen.clearCredential();
|
||||
await IMService.instance.logout();
|
||||
if (!mounted) return;
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
MaterialPageRoute(builder: (_) => const LoginScreen()),
|
||||
(route) => false,
|
||||
);
|
||||
},
|
||||
child: const Text('退出', style: TextStyle(color: AppColors.danger)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user