76f266645d
含 Phase 1.1 IM seq 排序、断线重连、多端已读同步与微信式语音转文字 UI。 排除 node_modules、构建产物、安装包与 .env 密钥。 Co-authored-by: Cursor <cursoragent@cursor.com>
167 lines
6.0 KiB
Dart
167 lines
6.0 KiB
Dart
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<DeskProfilePage> createState() => _DeskProfilePageState();
|
|
}
|
|
|
|
class _DeskProfilePageState extends State<DeskProfilePage> {
|
|
AppRelease? _rel;
|
|
bool _checking = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
widget.session.addListener(() {
|
|
if (mounted) setState(() {});
|
|
});
|
|
_peek();
|
|
}
|
|
|
|
Future<void> _peek() async {
|
|
final rel = await OtaUpdater(widget.api).fetch();
|
|
if (mounted) setState(() => _rel = rel);
|
|
}
|
|
|
|
Future<void> _logout() async {
|
|
try {
|
|
await widget.api.post('/auth/logout', {'refreshToken': widget.session.refreshToken});
|
|
} catch (_) {}
|
|
await widget.session.clear();
|
|
}
|
|
|
|
Future<void> _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<Widget> 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),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|