import 'package:flutter/material.dart'; import '../../api/oa_client.dart'; import '../../app_config.dart'; import '../../ota/updater.dart'; import 'desk_legal_page.dart'; import 'desk_profile_edit_page.dart'; import '../../session/session.dart'; import '../desk_theme.dart'; import '../widgets/desk_widgets.dart'; class DeskProfilePage extends StatefulWidget { const DeskProfilePage({super.key, required this.session, required this.api}); final SessionStore session; final OaClient api; @override State createState() => _DeskProfilePageState(); } class _DeskProfilePageState extends State { AppRelease? _rel; bool _checking = false; @override void initState() { super.initState(); widget.session.addListener(() { if (mounted) setState(() {}); }); _peek(); } Future _peek() async { final rel = await OtaUpdater(widget.api).fetch(); if (mounted) setState(() => _rel = rel); } Future _logout() async { try { await widget.api.post('/auth/logout', {'refreshToken': widget.session.refreshToken}); } catch (_) {} await widget.session.clear(); } Future _checkUpdate() async { setState(() => _checking = true); try { final rel = await OtaUpdater(widget.api).fetch(); if (!mounted) return; setState(() => _rel = rel); if (rel != null) await OtaUpdater(widget.api).prompt(context, rel, manual: true); } finally { if (mounted) setState(() => _checking = false); } } @override Widget build(BuildContext context) { final s = widget.session; return ColoredBox( color: kDeskBg, child: Column( children: [ const DeskPaneHeader(title: '我'), Expanded( child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 560), child: ListView( padding: const EdgeInsets.all(24), children: [ Material( color: Colors.white, borderRadius: BorderRadius.circular(8), child: InkWell( onTap: () => Navigator.of(context).push(MaterialPageRoute( builder: (_) => DeskProfileEditPage(session: s, api: widget.api), )), borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.all(16), child: Row( children: [ DeskAvatar( label: s.displayName, fileId: '${s.user['avatarFileId'] ?? ''}', api: widget.api, size: 56, ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(s.displayName, style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600)), const SizedBox(height: 4), Text('${s.user['department'] ?? ''} ${s.user['title'] ?? ''}'.trim(), style: const TextStyle(color: kDeskMute, fontSize: 13)), ], ), ), const Icon(Icons.chevron_right, color: kDeskMute), ], ), ), ), ), const SizedBox(height: 12), _section([ _row('检查更新', trailing: _rel?.newer == true ? '有新版本' : '已是最新', onTap: _checking ? null : _checkUpdate), _row('用户协议', onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (_) => DeskLegalPage(api: widget.api, kind: 'terms')))), _row('隐私政策', onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (_) => DeskLegalPage(api: widget.api, kind: 'privacy')))), ]), const SizedBox(height: 12), _section([ _row('版本', trailing: 'v${AppConfig.version}'), ]), const SizedBox(height: 20), SizedBox( width: double.infinity, child: OutlinedButton( style: OutlinedButton.styleFrom(minimumSize: const Size.fromHeight(40)), onPressed: _logout, child: const Text('退出登录', style: TextStyle(color: Color(0xFFFA5151))), ), ), ], ), ), ), ), ], ), ); } Widget _section(List children) { return Material( color: Colors.white, borderRadius: BorderRadius.circular(8), clipBehavior: Clip.antiAlias, child: Column(children: children), ); } Widget _row(String title, {String? trailing, VoidCallback? onTap}) { return Material( color: Colors.transparent, child: InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), child: Row( children: [ Expanded(child: Text(title, style: const TextStyle(fontSize: 14))), if (trailing != null) Text(trailing, style: const TextStyle(fontSize: 13, color: kDeskMute)), if (onTap != null) const Icon(Icons.chevron_right, size: 18, color: kDeskMute), ], ), ), ), ); } }