feat(apps): 重构 UI 架构为 presentation 层并新增 l10n 国际化支持
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import '../../../../core/l10n/l10n.dart';
|
||||
|
||||
class PersonMeta {
|
||||
final String? source;
|
||||
final double? confidence;
|
||||
@@ -546,15 +548,15 @@ class UserMemoryContent {
|
||||
parts.add(occupation!);
|
||||
}
|
||||
if (people.isNotEmpty) {
|
||||
parts.add('${people.length} 位联系人');
|
||||
parts.add(L10n.current.memorySummaryContactsCount(people.length));
|
||||
}
|
||||
if (places.isNotEmpty) {
|
||||
parts.add('${places.length} 个地点');
|
||||
parts.add(L10n.current.memorySummaryPlacesCount(places.length));
|
||||
}
|
||||
if (interests.isNotEmpty) {
|
||||
parts.add('${interests.length} 个兴趣');
|
||||
parts.add(L10n.current.memorySummaryInterestsCount(interests.length));
|
||||
}
|
||||
return parts.isEmpty ? '暂无信息' : parts.join(' · ');
|
||||
return parts.isEmpty ? L10n.current.memoryNoInfo : parts.join(' · ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -916,15 +918,17 @@ class WorkProfileContent {
|
||||
parts.add(occupation!);
|
||||
}
|
||||
if (expertise.isNotEmpty) {
|
||||
parts.add('${expertise.length} 项专长');
|
||||
parts.add(L10n.current.memorySummaryExpertiseCount(expertise.length));
|
||||
}
|
||||
if (currentProjects.isNotEmpty) {
|
||||
parts.add('${currentProjects.length} 个项目');
|
||||
parts.add(
|
||||
L10n.current.memorySummaryProjectsCount(currentProjects.length),
|
||||
);
|
||||
}
|
||||
if (teamMembers.isNotEmpty) {
|
||||
parts.add('${teamMembers.length} 位团队成员');
|
||||
parts.add(L10n.current.memorySummaryTeamMembersCount(teamMembers.length));
|
||||
}
|
||||
return parts.isEmpty ? '暂无信息' : parts.join(' · ');
|
||||
return parts.isEmpty ? L10n.current.memoryNoInfo : parts.join(' · ');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:social_app/core/api/i_api_client.dart';
|
||||
import 'package:social_app/core/network/i_api_client.dart';
|
||||
import '../models/automation_job_model.dart';
|
||||
|
||||
class AutomationJobsApi {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:social_app/core/api/i_api_client.dart';
|
||||
import 'package:social_app/core/network/i_api_client.dart';
|
||||
import '../models/memory_models.dart';
|
||||
|
||||
class MemoryService {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../users/data/models/user_response.dart';
|
||||
import '../../../contacts/data/users/models/user_response.dart';
|
||||
import 'user_profile_cache_repository.dart';
|
||||
|
||||
class SettingsUserCache {
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'dart:async';
|
||||
import '../../../../core/cache/cache_entry.dart';
|
||||
import '../../../../core/cache/cache_policy.dart';
|
||||
import '../../../../core/cache/hybrid_cache_store.dart';
|
||||
import '../../../users/data/models/user_response.dart';
|
||||
import '../../../contacts/data/users/models/user_response.dart';
|
||||
|
||||
class UserProfileCacheRepository {
|
||||
static const String cacheKey = 'settings:user_profile';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:social_app/core/api/i_api_client.dart';
|
||||
import 'package:social_app/core/network/i_api_client.dart';
|
||||
|
||||
class AppVersionResponse {
|
||||
final bool hasUpdate;
|
||||
|
||||
+57
-20
@@ -2,15 +2,16 @@ import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../core/di/injection.dart';
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../shared/widgets/app_button.dart';
|
||||
import '../../../../shared/widgets/app_loading_indicator.dart';
|
||||
import '../../../../shared/widgets/toast/toast.dart';
|
||||
import '../../../../shared/widgets/toast/toast_type.dart';
|
||||
import '../../data/services/settings_user_cache.dart';
|
||||
import '../../../users/data/models/user_response.dart';
|
||||
import '../../../users/data/users_api.dart';
|
||||
import '../../../contacts/data/users/models/user_response.dart';
|
||||
import '../../../contacts/data/users/users_api.dart';
|
||||
import '../widgets/account_section_card.dart';
|
||||
import '../widgets/settings_page_scaffold.dart';
|
||||
|
||||
@@ -69,7 +70,11 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
Toast.show(context, '加载用户信息失败', type: ToastType.error);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsEditProfileLoadFailed,
|
||||
type: ToastType.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,13 +117,21 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
try {
|
||||
await _usersApi.uploadAvatar(_selectedAvatar!);
|
||||
if (mounted) {
|
||||
Toast.show(context, '头像上传成功', type: ToastType.success);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsEditProfileAvatarUploadSuccess,
|
||||
type: ToastType.success,
|
||||
);
|
||||
_selectedAvatar = null;
|
||||
await _loadUser();
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
Toast.show(context, '头像上传失败,请重试', type: ToastType.error);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsEditProfileAvatarUploadFailed,
|
||||
type: ToastType.error,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
@@ -139,11 +152,19 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
|
||||
if (usernameChanged) {
|
||||
if (newUsername.isEmpty) {
|
||||
Toast.show(context, '用户名不能为空', type: ToastType.warning);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsEditProfileUsernameRequired,
|
||||
type: ToastType.warning,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (newUsername.length < 3 || newUsername.length > 30) {
|
||||
Toast.show(context, '用户名需要3-30个字符', type: ToastType.warning);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsEditProfileUsernameLengthInvalid,
|
||||
type: ToastType.warning,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -167,12 +188,20 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
Toast.show(context, '保存成功', type: ToastType.success);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsEditProfileSaveSuccess,
|
||||
type: ToastType.success,
|
||||
);
|
||||
context.pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
Toast.show(context, '保存失败,请重试', type: ToastType.error);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsEditProfileSaveFailed,
|
||||
type: ToastType.error,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
@@ -192,8 +221,10 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
|
||||
return SettingsPageScaffold(
|
||||
title: '编辑资料',
|
||||
title: l10n.settingsEditProfileTitle,
|
||||
onBack: () => context.pop(),
|
||||
resizeOnKeyboard: false,
|
||||
maintainBottomViewPadding: true,
|
||||
@@ -213,7 +244,7 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
width: double.infinity,
|
||||
height: 52,
|
||||
child: AppButton(
|
||||
text: '保存修改',
|
||||
text: l10n.settingsEditProfileSaveChanges,
|
||||
onPressed: _hasChanges && !_isSaving ? _saveProfile : null,
|
||||
isLoading: _isSaving,
|
||||
),
|
||||
@@ -222,8 +253,10 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
}
|
||||
|
||||
Widget _buildBasicInfoSection() {
|
||||
final l10n = context.l10n;
|
||||
|
||||
return AccountSectionCard(
|
||||
title: '基础信息',
|
||||
title: l10n.settingsEditProfileBasicInfo,
|
||||
backgroundColor: AppColors.white,
|
||||
borderColor: AppColors.borderSecondary,
|
||||
child: Column(
|
||||
@@ -231,8 +264,8 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
children: [
|
||||
_buildAvatarSection(),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
const Text(
|
||||
'用户名',
|
||||
Text(
|
||||
l10n.settingsEditProfileUsername,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -244,7 +277,9 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
controller: _usernameController,
|
||||
onChanged: (_) => _onFieldChanged(),
|
||||
style: const TextStyle(fontSize: 15, color: AppColors.slate900),
|
||||
decoration: _buildInputDecoration('请输入用户名'),
|
||||
decoration: _buildInputDecoration(
|
||||
l10n.settingsEditProfileUsernameHint,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -333,15 +368,17 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
}
|
||||
|
||||
Widget _buildBioSection() {
|
||||
final l10n = context.l10n;
|
||||
|
||||
return AccountSectionCard(
|
||||
title: '个人简介',
|
||||
title: l10n.settingsEditProfileBio,
|
||||
backgroundColor: AppColors.white,
|
||||
borderColor: AppColors.borderSecondary,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'简介内容',
|
||||
Text(
|
||||
l10n.settingsEditProfileBioContent,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -356,7 +393,7 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
|
||||
maxLength: 200,
|
||||
style: const TextStyle(fontSize: 15, color: AppColors.slate900),
|
||||
decoration: _buildInputDecoration(
|
||||
'介绍一下自己吧',
|
||||
l10n.settingsEditProfileBioHint,
|
||||
).copyWith(contentPadding: const EdgeInsets.all(AppSpacing.lg)),
|
||||
),
|
||||
],
|
||||
+20
-11
@@ -1,8 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../../../core/di/injection.dart';
|
||||
import '../../../../core/router/app_routes.dart';
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../app/router/app_routes.dart';
|
||||
import '../../../../core/l10n/l10n.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../shared/widgets/app_button.dart';
|
||||
import '../../../../shared/widgets/app_loading_indicator.dart';
|
||||
@@ -43,7 +44,7 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
|
||||
return BlocProvider.value(
|
||||
value: _cubit,
|
||||
child: SettingsPageScaffold(
|
||||
title: '周期计划',
|
||||
title: context.l10n.settingsFeaturesTitle,
|
||||
onBack: () => context.pop(),
|
||||
body: BlocBuilder<AutomationJobsCubit, AutomationJobsState>(
|
||||
builder: (context, state) {
|
||||
@@ -67,17 +68,17 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('每日'),
|
||||
_buildSectionTitle(context.l10n.settingsSectionDaily),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
if (dailyJobs.isEmpty)
|
||||
_buildEmptyHint('暂无每日计划')
|
||||
_buildEmptyHint(context.l10n.settingsNoDailyPlans)
|
||||
else
|
||||
...dailyJobs.map(_buildJobCard),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
_buildSectionTitle('每周'),
|
||||
_buildSectionTitle(context.l10n.settingsSectionWeekly),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
if (weeklyJobs.isEmpty)
|
||||
_buildEmptyHint('暂无每周计划')
|
||||
_buildEmptyHint(context.l10n.settingsNoWeeklyPlans)
|
||||
else
|
||||
...weeklyJobs.map(_buildJobCard),
|
||||
if (state.canCreateMore) ...[
|
||||
@@ -181,7 +182,11 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
|
||||
value: job.isActive,
|
||||
onChanged: (next) {
|
||||
if (job.isSystem) {
|
||||
Toast.show(context, '系统预置任务状态不可修改', type: ToastType.info);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsSystemJobReadonly,
|
||||
type: ToastType.info,
|
||||
);
|
||||
return;
|
||||
}
|
||||
_cubit.updateJobStatus(id: job.id, enabled: next);
|
||||
@@ -194,14 +199,18 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
|
||||
}
|
||||
|
||||
String _buildSubtitle(AutomationJobModel job) {
|
||||
final statusText = job.isActive ? '已启用' : '未启用';
|
||||
final sourceText = job.isSystem ? '系统预置' : '自定义';
|
||||
final statusText = job.isActive
|
||||
? context.l10n.settingsJobStatusEnabled
|
||||
: context.l10n.settingsJobStatusDisabled;
|
||||
final sourceText = job.isSystem
|
||||
? context.l10n.settingsJobSourceSystem
|
||||
: context.l10n.settingsJobSourceCustom;
|
||||
return '$sourceText • $statusText';
|
||||
}
|
||||
|
||||
Widget _buildCreateButton() {
|
||||
return AppButton(
|
||||
text: '创建任务',
|
||||
text: context.l10n.settingsCreateJob,
|
||||
onPressed: () async {
|
||||
await context.push(AppRoutes.settingsJobNew);
|
||||
if (!mounted) {
|
||||
+146
-70
@@ -3,8 +3,9 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
|
||||
import '../../../../core/di/injection.dart';
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../shared/widgets/app_button.dart';
|
||||
import '../../../../shared/widgets/app_input.dart';
|
||||
@@ -15,7 +16,7 @@ import '../../../../shared/widgets/detail_header_action_menu.dart';
|
||||
import '../../../../shared/widgets/destructive_action_sheet.dart';
|
||||
import '../../../../shared/widgets/toast/toast.dart';
|
||||
import '../../../../shared/widgets/toast/toast_type.dart';
|
||||
import '../../../../shared/utils/tool_name_localizer.dart';
|
||||
import '../../../../core/utils/tool_name_localizer.dart';
|
||||
import '../../data/models/automation_job_model.dart';
|
||||
import '../../data/services/automation_jobs_api.dart';
|
||||
import '../../presentation/cubits/job_detail_cubit.dart';
|
||||
@@ -74,9 +75,10 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
final l10n = context.l10n;
|
||||
if (state.isLoading) {
|
||||
return SettingsPageScaffold(
|
||||
title: '加载中',
|
||||
title: l10n.commonLoading,
|
||||
body: const Center(child: AppLoadingIndicator()),
|
||||
);
|
||||
}
|
||||
@@ -85,14 +87,14 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
final isEditMode = widget.jobId != null;
|
||||
if (isEditMode && job == null && state.error != null) {
|
||||
return SettingsPageScaffold(
|
||||
title: '任务详情',
|
||||
title: l10n.settingsJobDetailTitle,
|
||||
onBack: () => context.pop(),
|
||||
body: _buildLoadFailedView(state.error!),
|
||||
);
|
||||
}
|
||||
|
||||
return SettingsPageScaffold(
|
||||
title: job?.title ?? '新建周期计划',
|
||||
title: job?.title ?? l10n.settingsJobCreatePageTitle,
|
||||
onBack: () => context.pop(),
|
||||
trailing: job != null && !job.isSystem
|
||||
? _buildHeaderActions(job.id, state)
|
||||
@@ -107,10 +109,11 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Widget _buildLoadFailedView(String error) {
|
||||
final l10n = context.l10n;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('加载失败'),
|
||||
_buildSectionTitle(l10n.settingsJobLoadFailed),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
Text(
|
||||
error,
|
||||
@@ -121,43 +124,64 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
AppButton(text: '重试', onPressed: () => _cubit.loadJob(widget.jobId!)),
|
||||
AppButton(
|
||||
text: l10n.settingsJobRetry,
|
||||
onPressed: () => _cubit.loadJob(widget.jobId!),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDetailPage(AutomationJobModel job, JobDetailState state) {
|
||||
final l10n = context.l10n;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildOverviewCard(job),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
_buildSectionTitle('计划配置'),
|
||||
_buildSectionTitle(l10n.settingsJobPlanConfig),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildInfoCard([
|
||||
_buildInfoRow('周期', _scheduleLabel(job.config.schedule.type)),
|
||||
_buildInfoRow('执行时间', _displayRunAt(job.config.schedule)),
|
||||
_buildInfoRow('时区', job.timezone),
|
||||
_buildInfoRow('状态', job.isActive ? '已启用' : '未启用'),
|
||||
_buildInfoRow(
|
||||
l10n.settingsJobCycle,
|
||||
_scheduleLabel(job.config.schedule.type),
|
||||
),
|
||||
_buildInfoRow(
|
||||
l10n.settingsJobRunAt,
|
||||
_displayRunAt(job.config.schedule),
|
||||
),
|
||||
_buildInfoRow(l10n.settingsJobTimezone, job.timezone),
|
||||
_buildInfoRow(
|
||||
l10n.settingsJobStatusLabel,
|
||||
job.isActive
|
||||
? l10n.settingsJobStatusEnabled
|
||||
: l10n.settingsJobStatusDisabled,
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
_buildSectionTitle('输入模板'),
|
||||
_buildSectionTitle(l10n.settingsJobInputTemplate),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildTextBlock(job.config.inputTemplate),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
_buildSectionTitle('启用工具'),
|
||||
_buildSectionTitle(l10n.settingsJobEnabledTools),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildToolWrap(job.config.enabledTools),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
_buildSectionTitle('上下文消息模式'),
|
||||
_buildSectionTitle(l10n.settingsJobContextMode),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildInfoCard([
|
||||
_buildInfoRow('来源', _contextSourceLabel(job.config.context.source)),
|
||||
_buildInfoRow(
|
||||
'窗口模式',
|
||||
l10n.settingsJobContextSource,
|
||||
_contextSourceLabel(job.config.context.source),
|
||||
),
|
||||
_buildInfoRow(
|
||||
l10n.settingsJobWindowMode,
|
||||
_windowModeLabel(job.config.context.windowMode),
|
||||
),
|
||||
_buildInfoRow('窗口数量', '${job.config.context.windowCount}'),
|
||||
_buildInfoRow(
|
||||
l10n.settingsJobWindowCount,
|
||||
l10n.settingsJobWindowCountValue(job.config.context.windowCount),
|
||||
),
|
||||
]),
|
||||
if (!job.isSystem && state.isSaving)
|
||||
const Padding(
|
||||
@@ -169,11 +193,12 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Widget _buildHeaderActions(String jobId, JobDetailState state) {
|
||||
final l10n = context.l10n;
|
||||
return DetailHeaderActionMenu<_JobDetailHeaderAction>(
|
||||
items: const [
|
||||
items: [
|
||||
DetailHeaderActionItem<_JobDetailHeaderAction>(
|
||||
value: _JobDetailHeaderAction.delete,
|
||||
label: '删除',
|
||||
label: l10n.commonDelete,
|
||||
icon: Icons.delete_outline,
|
||||
isDestructive: true,
|
||||
),
|
||||
@@ -190,11 +215,12 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Future<void> _confirmAndDelete(String jobId) async {
|
||||
final l10n = context.l10n;
|
||||
final confirmed = await showDestructiveActionSheet(
|
||||
context,
|
||||
title: '删除周期计划',
|
||||
message: '删除后将无法恢复,是否继续?',
|
||||
confirmText: '确认删除',
|
||||
title: l10n.settingsJobDeleteTitle,
|
||||
message: l10n.settingsJobDeleteMessage,
|
||||
confirmText: l10n.settingsJobDeleteConfirm,
|
||||
);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
@@ -204,12 +230,17 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
return;
|
||||
}
|
||||
if (success) {
|
||||
Toast.show(context, '删除成功', type: ToastType.success);
|
||||
Toast.show(
|
||||
context,
|
||||
l10n.settingsJobDeleteSuccess,
|
||||
type: ToastType.success,
|
||||
);
|
||||
context.pop();
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildOverviewCard(AutomationJobModel job) {
|
||||
final l10n = context.l10n;
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
@@ -238,8 +269,16 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
spacing: AppSpacing.sm,
|
||||
runSpacing: AppSpacing.sm,
|
||||
children: [
|
||||
_buildBadge(job.isSystem ? '系统预置' : '自定义'),
|
||||
_buildBadge(job.isActive ? '已启用' : '未启用'),
|
||||
_buildBadge(
|
||||
job.isSystem
|
||||
? l10n.settingsJobSourceSystem
|
||||
: l10n.settingsJobSourceCustom,
|
||||
),
|
||||
_buildBadge(
|
||||
job.isActive
|
||||
? l10n.settingsJobStatusEnabled
|
||||
: l10n.settingsJobStatusDisabled,
|
||||
),
|
||||
_buildBadge(_scheduleLabel(job.config.schedule.type)),
|
||||
],
|
||||
),
|
||||
@@ -271,6 +310,7 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Widget _buildCreateForm(JobDetailState state) {
|
||||
final l10n = context.l10n;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -283,7 +323,7 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
_buildCreateContextSection(),
|
||||
const SizedBox(height: AppSpacing.xl),
|
||||
AppButton(
|
||||
text: '创建任务',
|
||||
text: l10n.settingsCreateJob,
|
||||
isLoading: state.isSaving,
|
||||
onPressed: state.isSaving ? null : _submitCreate,
|
||||
),
|
||||
@@ -292,16 +332,21 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Widget _buildCreateBasicSection() {
|
||||
final l10n = context.l10n;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('基本信息'),
|
||||
_buildSectionTitle(l10n.settingsJobBasicInfo),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
AppInput(label: '任务名称', hint: '请输入任务名称', controller: _titleController),
|
||||
AppInput(
|
||||
label: l10n.settingsJobName,
|
||||
hint: l10n.settingsJobNameHint,
|
||||
controller: _titleController,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
AppInput(
|
||||
label: '输入模板',
|
||||
hint: '例如:请总结今天的记忆内容',
|
||||
label: l10n.settingsJobInputTemplate,
|
||||
hint: l10n.settingsJobTemplateHint,
|
||||
controller: _templateController,
|
||||
maxLines: 4,
|
||||
),
|
||||
@@ -310,13 +355,14 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Widget _buildCreateRuleSection() {
|
||||
final l10n = context.l10n;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('执行规则'),
|
||||
_buildSectionTitle(l10n.settingsJobExecutionRules),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildPickerTile(
|
||||
label: '周期',
|
||||
label: l10n.settingsJobCycle,
|
||||
value: _scheduleLabel(_scheduleType),
|
||||
onTap: _pickScheduleType,
|
||||
),
|
||||
@@ -326,21 +372,26 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
],
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildPickerTile(
|
||||
label: '执行时间',
|
||||
label: l10n.settingsJobRunAt,
|
||||
value: _formatTime(_runAt),
|
||||
onTap: _pickRunAt,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildPickerTile(label: '时区', value: _timezone, onTap: _pickTimezone),
|
||||
_buildPickerTile(
|
||||
label: l10n.settingsJobTimezone,
|
||||
value: _timezone,
|
||||
onTap: _pickTimezone,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCreateToolSection() {
|
||||
final l10n = context.l10n;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('工具选择'),
|
||||
_buildSectionTitle(l10n.settingsJobToolSelection),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildToolSelector(),
|
||||
],
|
||||
@@ -348,25 +399,26 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Widget _buildCreateContextSection() {
|
||||
final l10n = context.l10n;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('上下文消息模式'),
|
||||
_buildSectionTitle(l10n.settingsJobContextMode),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildPickerTile(
|
||||
label: '来源',
|
||||
label: l10n.settingsJobContextSource,
|
||||
value: _contextSourceLabel(_contextSource),
|
||||
onTap: _pickContextSource,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildPickerTile(
|
||||
label: '窗口模式',
|
||||
label: l10n.settingsJobWindowMode,
|
||||
value: _windowModeLabel(_contextWindowMode),
|
||||
onTap: _pickWindowMode,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildCounterTile(
|
||||
label: '窗口数量',
|
||||
label: l10n.settingsJobWindowCount,
|
||||
value: _contextWindowCount,
|
||||
onMinus: _contextWindowCount > 1
|
||||
? () {
|
||||
@@ -458,7 +510,7 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'$label:$value',
|
||||
context.l10n.settingsJobCounterValue(label, value),
|
||||
style: const TextStyle(
|
||||
color: AppColors.slate800,
|
||||
fontSize: 14,
|
||||
@@ -542,14 +594,15 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Widget _buildWeekdaySelector() {
|
||||
const weekdayLabels = <int, String>{
|
||||
1: '周一',
|
||||
2: '周二',
|
||||
3: '周三',
|
||||
4: '周四',
|
||||
5: '周五',
|
||||
6: '周六',
|
||||
7: '周日',
|
||||
final l10n = context.l10n;
|
||||
final weekdayLabels = <int, String>{
|
||||
1: l10n.settingsJobWeekdayMon,
|
||||
2: l10n.settingsJobWeekdayTue,
|
||||
3: l10n.settingsJobWeekdayWed,
|
||||
4: l10n.settingsJobWeekdayThu,
|
||||
5: l10n.settingsJobWeekdayFri,
|
||||
6: l10n.settingsJobWeekdaySat,
|
||||
7: l10n.settingsJobWeekdaySun,
|
||||
};
|
||||
|
||||
return Container(
|
||||
@@ -562,8 +615,8 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'执行日',
|
||||
Text(
|
||||
l10n.settingsJobRunDays,
|
||||
style: TextStyle(
|
||||
color: AppColors.slate500,
|
||||
fontSize: 12,
|
||||
@@ -622,7 +675,7 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
|
||||
Widget _buildToolWrap(List<String> tools) {
|
||||
if (tools.isEmpty) {
|
||||
return _buildTextBlock('未启用工具');
|
||||
return _buildTextBlock(context.l10n.settingsJobNoToolsEnabled);
|
||||
}
|
||||
return Wrap(
|
||||
spacing: AppSpacing.sm,
|
||||
@@ -713,13 +766,17 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Future<void> _pickScheduleType() async {
|
||||
final l10n = context.l10n;
|
||||
final picked = await showAppSelectionSheet<String>(
|
||||
context,
|
||||
title: '选择周期',
|
||||
title: l10n.settingsJobPickCycle,
|
||||
selectedValue: _scheduleType,
|
||||
items: const [
|
||||
AppSelectionItem(value: 'daily', label: '每日'),
|
||||
AppSelectionItem(value: 'weekly', label: '每周'),
|
||||
items: [
|
||||
AppSelectionItem(value: 'daily', label: l10n.settingsJobScheduleDaily),
|
||||
AppSelectionItem(
|
||||
value: 'weekly',
|
||||
label: l10n.settingsJobScheduleWeekly,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (picked != null) {
|
||||
@@ -735,7 +792,7 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
Future<void> _pickTimezone() async {
|
||||
final picked = await showAppSelectionSheet<String>(
|
||||
context,
|
||||
title: '选择时区',
|
||||
title: context.l10n.settingsJobPickTimezone,
|
||||
selectedValue: _timezone,
|
||||
items: const [
|
||||
AppSelectionItem(value: 'Asia/Shanghai', label: 'Asia/Shanghai'),
|
||||
@@ -750,11 +807,17 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Future<void> _pickContextSource() async {
|
||||
final l10n = context.l10n;
|
||||
final picked = await showAppSelectionSheet<String>(
|
||||
context,
|
||||
title: '选择上下文来源',
|
||||
title: l10n.settingsJobPickContextSource,
|
||||
selectedValue: _contextSource,
|
||||
items: const [AppSelectionItem(value: 'latest_chat', label: '最近聊天')],
|
||||
items: [
|
||||
AppSelectionItem(
|
||||
value: 'latest_chat',
|
||||
label: l10n.settingsJobContextSourceLatestChat,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
@@ -764,13 +827,17 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
Future<void> _pickWindowMode() async {
|
||||
final l10n = context.l10n;
|
||||
final picked = await showAppSelectionSheet<String>(
|
||||
context,
|
||||
title: '选择窗口模式',
|
||||
title: l10n.settingsJobPickWindowMode,
|
||||
selectedValue: _contextWindowMode,
|
||||
items: const [
|
||||
AppSelectionItem(value: 'day', label: '按天数'),
|
||||
AppSelectionItem(value: 'number', label: '按消息数'),
|
||||
items: [
|
||||
AppSelectionItem(value: 'day', label: l10n.settingsJobWindowModeByDay),
|
||||
AppSelectionItem(
|
||||
value: 'number',
|
||||
label: l10n.settingsJobWindowModeByNumber,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (picked != null) {
|
||||
@@ -802,29 +869,30 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
}
|
||||
|
||||
String _scheduleLabel(String scheduleType) {
|
||||
final l10n = context.l10n;
|
||||
final normalized = scheduleType.toLowerCase();
|
||||
if (normalized == 'daily') {
|
||||
return '每日';
|
||||
return l10n.settingsJobScheduleDaily;
|
||||
}
|
||||
if (normalized == 'weekly') {
|
||||
return '每周';
|
||||
return l10n.settingsJobScheduleWeekly;
|
||||
}
|
||||
return scheduleType;
|
||||
}
|
||||
|
||||
String _contextSourceLabel(String source) {
|
||||
if (source == 'latest_chat') {
|
||||
return '最近聊天';
|
||||
return context.l10n.settingsJobContextSourceLatestChat;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
String _windowModeLabel(String mode) {
|
||||
if (mode == 'day') {
|
||||
return '按天数';
|
||||
return context.l10n.settingsJobWindowModeByDay;
|
||||
}
|
||||
if (mode == 'number') {
|
||||
return '按消息数';
|
||||
return context.l10n.settingsJobWindowModeByNumber;
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
@@ -833,7 +901,11 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
final title = _titleController.text.trim();
|
||||
final template = _templateController.text.trim();
|
||||
if (title.isEmpty || template.isEmpty) {
|
||||
Toast.show(context, '请填写完整信息', type: ToastType.error);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsJobFillRequired,
|
||||
type: ToastType.error,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -863,7 +935,11 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
|
||||
return;
|
||||
}
|
||||
if (success) {
|
||||
Toast.show(context, '创建成功', type: ToastType.success);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsJobCreateSuccess,
|
||||
type: ToastType.success,
|
||||
);
|
||||
context.pop(true);
|
||||
}
|
||||
}
|
||||
+36
-21
@@ -1,8 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:social_app/core/di/injection.dart';
|
||||
import 'package:social_app/app/di/injection.dart';
|
||||
import 'package:social_app/core/theme/design_tokens.dart';
|
||||
import 'package:social_app/core/router/app_routes.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
import 'package:social_app/app/router/app_routes.dart';
|
||||
import 'package:social_app/shared/widgets/app_loading_indicator.dart';
|
||||
import 'package:social_app/shared/widgets/app_pressable.dart';
|
||||
import '../widgets/settings_page_scaffold.dart';
|
||||
@@ -45,7 +46,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_error = '加载失败,请重试';
|
||||
_error = L10n.current.memoryLoadFailedRetry;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
@@ -54,7 +55,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SettingsPageScaffold(
|
||||
title: '我的记忆',
|
||||
title: context.l10n.memoryTitle,
|
||||
onBack: () => context.pop(),
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -127,8 +128,8 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'智能记忆',
|
||||
Text(
|
||||
context.l10n.memorySmartTitle,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -137,7 +138,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'持续学习你的偏好和习惯',
|
||||
context.l10n.memorySmartDesc,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -169,7 +170,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
Icon(Icons.error_outline, size: 48, color: AppColors.slate300),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text(
|
||||
_error ?? '加载失败',
|
||||
_error ?? context.l10n.memoryLoadFailedRetry,
|
||||
style: TextStyle(fontSize: 14, color: AppColors.slate500),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
@@ -186,9 +187,9 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
border: Border.all(color: AppColors.blue100),
|
||||
),
|
||||
child: const Text(
|
||||
'重新加载',
|
||||
style: TextStyle(
|
||||
child: Text(
|
||||
context.l10n.memoryReload,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.blue600,
|
||||
@@ -205,11 +206,11 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildSectionLabel('用户记忆'),
|
||||
_buildSectionLabel(context.l10n.memorySectionUser),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildUserMemoryCard(),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionLabel('工作记忆'),
|
||||
_buildSectionLabel(context.l10n.memorySectionWork),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildWorkMemoryCard(),
|
||||
],
|
||||
@@ -285,8 +286,8 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'个人偏好',
|
||||
Text(
|
||||
context.l10n.memoryUserProfile,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -295,7 +296,9 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
hasData ? userMemory.summary : '暂无信息',
|
||||
hasData
|
||||
? userMemory.summary
|
||||
: context.l10n.memoryNoInfo,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -325,7 +328,12 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
'${userMemory.interests.length}',
|
||||
'${userMemory.recurringRoutines.length}',
|
||||
],
|
||||
labels: ['联系人', '地点', '兴趣', '日程'],
|
||||
labels: [
|
||||
context.l10n.memoryStatContacts,
|
||||
context.l10n.memoryStatPlaces,
|
||||
context.l10n.memoryStatInterests,
|
||||
context.l10n.memoryStatSchedule,
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
@@ -389,8 +397,8 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'工作画像',
|
||||
Text(
|
||||
context.l10n.memoryWorkProfile,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -399,7 +407,9 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
hasData ? workMemory.summary : '暂无信息',
|
||||
hasData
|
||||
? workMemory.summary
|
||||
: context.l10n.memoryNoInfo,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -429,7 +439,12 @@ class _MemoryScreenState extends State<MemoryScreen> {
|
||||
'${workMemory.currentProjects.length}',
|
||||
'${workMemory.teamMembers.length}',
|
||||
],
|
||||
labels: ['专长', '工具', '项目', '团队'],
|
||||
labels: [
|
||||
context.l10n.memoryStatExpertise,
|
||||
context.l10n.memoryStatTools,
|
||||
context.l10n.memoryStatProjects,
|
||||
context.l10n.memoryStatTeam,
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
+60
-54
@@ -2,8 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:social_app/core/constants/app_constants.dart';
|
||||
import 'package:social_app/core/di/injection.dart';
|
||||
import 'package:social_app/core/router/app_routes.dart';
|
||||
import 'package:social_app/app/di/injection.dart';
|
||||
import 'package:social_app/app/router/app_routes.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
import 'package:social_app/core/theme/design_tokens.dart';
|
||||
import 'package:social_app/shared/widgets/app_button.dart';
|
||||
import 'package:social_app/shared/widgets/app_loading_indicator.dart';
|
||||
@@ -11,16 +12,16 @@ import 'package:social_app/shared/widgets/app_pressable.dart';
|
||||
import 'package:social_app/shared/widgets/destructive_action_sheet.dart';
|
||||
import 'package:social_app/shared/widgets/toast/toast.dart';
|
||||
import 'package:social_app/shared/widgets/toast/toast_type.dart';
|
||||
import 'package:social_app/shared/utils/phone_display_formatter.dart';
|
||||
import 'package:social_app/core/utils/phone_display_formatter.dart';
|
||||
import 'package:social_app/features/auth/presentation/bloc/auth_bloc.dart';
|
||||
import 'package:social_app/features/auth/presentation/bloc/auth_event.dart';
|
||||
import 'package:social_app/features/auth/presentation/bloc/auth_state.dart';
|
||||
import 'package:social_app/features/friends/data/friends_api.dart';
|
||||
import 'package:social_app/features/contacts/data/friends_api.dart';
|
||||
import 'package:social_app/features/settings/data/settings_api.dart';
|
||||
import 'package:social_app/features/settings/data/services/automation_jobs_api.dart';
|
||||
import 'package:social_app/features/settings/data/services/settings_user_cache.dart';
|
||||
import 'package:social_app/features/users/data/models/user_response.dart';
|
||||
import 'package:social_app/features/home/ui/navigation/home_return_policy.dart';
|
||||
import 'package:social_app/features/contacts/data/users/models/user_response.dart';
|
||||
import 'package:social_app/features/home/presentation/navigation/home_return_policy.dart';
|
||||
import '../widgets/settings_page_scaffold.dart';
|
||||
|
||||
const settingsProfileEditButtonKey = ValueKey('settings_profile_edit_button');
|
||||
@@ -107,7 +108,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SettingsPageScaffold(
|
||||
title: '设置',
|
||||
title: context.l10n.settingsTitle,
|
||||
onBack: () => returnToHomePreserveState(context, forceGoHome: true),
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -126,20 +127,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionLabel(String label) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xs),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate500,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProfileHero() {
|
||||
if (_isLoading) {
|
||||
return Container(
|
||||
@@ -154,9 +141,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
final username = _user?.username ?? '未设置';
|
||||
final l10n = context.l10n;
|
||||
final username = _user?.username ?? l10n.settingsUnset;
|
||||
final phone = _user?.phone == null
|
||||
? '未设置'
|
||||
? l10n.settingsUnset
|
||||
: formatPhoneForDisplay(_user?.phone);
|
||||
|
||||
return Container(
|
||||
@@ -288,8 +276,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColors.borderQuaternary),
|
||||
),
|
||||
child: const Text(
|
||||
'Free',
|
||||
child: Text(
|
||||
context.l10n.settingsFreeBadge,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -355,23 +343,29 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
}
|
||||
|
||||
String _buildFriendsSubtitle() {
|
||||
final l10n = context.l10n;
|
||||
if (_friendsCount == 0) {
|
||||
return '暂无联系人';
|
||||
return l10n.settingsNoContacts;
|
||||
}
|
||||
if (_friendsCount == 1) {
|
||||
return '已添加 1 位:$_firstFriendName';
|
||||
return l10n.settingsContactsAddedOne(
|
||||
_firstFriendName ?? l10n.commonUnknown,
|
||||
);
|
||||
}
|
||||
return '已添加 $_friendsCount 位联系人';
|
||||
return l10n.settingsContactsAddedMany(_friendsCount);
|
||||
}
|
||||
|
||||
String _buildAutomationSubtitle() {
|
||||
final l10n = context.l10n;
|
||||
if (_enabledJobsCount == 0) {
|
||||
return '暂无启用计划';
|
||||
return l10n.settingsNoEnabledPlans;
|
||||
}
|
||||
if (_enabledJobsCount == 1) {
|
||||
return '已启用:${_firstEnabledJobTitle ?? '周期计划'}';
|
||||
return l10n.settingsEnabledPlanOne(
|
||||
_firstEnabledJobTitle ?? l10n.settingsFeaturesTitle,
|
||||
);
|
||||
}
|
||||
return '已启用 $_enabledJobsCount 个计划';
|
||||
return l10n.settingsEnabledPlanMany(_enabledJobsCount);
|
||||
}
|
||||
|
||||
Widget _buildQuickActions(BuildContext context) {
|
||||
@@ -381,7 +375,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
child: _buildActionCard(
|
||||
icon: Icons.people,
|
||||
iconColor: AppColors.blue500,
|
||||
title: '联系人',
|
||||
title: context.l10n.contactsTitle,
|
||||
subtitle: _buildFriendsSubtitle(),
|
||||
onTap: () => context.push(AppRoutes.contactsList),
|
||||
),
|
||||
@@ -391,7 +385,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
child: _buildActionCard(
|
||||
icon: Icons.auto_awesome,
|
||||
iconColor: AppColors.blue500,
|
||||
title: '周期计划',
|
||||
title: context.l10n.settingsFeaturesTitle,
|
||||
subtitle: _buildAutomationSubtitle(),
|
||||
onTap: () => context.push(AppRoutes.settingsFeatures),
|
||||
),
|
||||
@@ -519,8 +513,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'升级到 Pro',
|
||||
Text(
|
||||
context.l10n.settingsUpgradeProTitle,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -529,7 +523,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'解锁更多高级功能',
|
||||
context.l10n.settingsUpgradeProDesc,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -554,8 +548,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Text(
|
||||
'升级',
|
||||
child: Text(
|
||||
context.l10n.settingsUpgradeButton,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -579,19 +573,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
children: [
|
||||
_buildMenuItem(
|
||||
icon: Icons.notifications,
|
||||
title: '提醒设置',
|
||||
title: context.l10n.settingsMenuNotifications,
|
||||
onTap: () {},
|
||||
),
|
||||
_buildDivider(),
|
||||
_buildMenuItem(
|
||||
icon: Icons.bookmark,
|
||||
title: '我的记忆',
|
||||
title: context.l10n.memoryTitle,
|
||||
onTap: () => context.push(AppRoutes.settingsMemory),
|
||||
),
|
||||
_buildDivider(),
|
||||
_buildMenuItem(
|
||||
icon: Icons.system_update,
|
||||
title: '检查更新',
|
||||
title: context.l10n.settingsMenuCheckUpdates,
|
||||
trailing: 'v${AppConstants.version}',
|
||||
onTap: _checkForUpdates,
|
||||
),
|
||||
@@ -677,9 +671,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
Future<void> _onTapLogout() async {
|
||||
final confirmed = await showDestructiveActionSheet(
|
||||
context,
|
||||
title: '退出登录',
|
||||
message: '确定退出当前账户吗?',
|
||||
confirmText: '确认退出',
|
||||
title: context.l10n.settingsLogoutTitle,
|
||||
message: context.l10n.settingsLogoutConfirmMessage,
|
||||
confirmText: context.l10n.settingsLogoutConfirm,
|
||||
);
|
||||
if (!confirmed || !mounted) {
|
||||
return;
|
||||
@@ -694,7 +688,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
.timeout(const Duration(seconds: 5));
|
||||
} catch (_) {
|
||||
if (!mounted) return;
|
||||
Toast.show(context, '退出失败,请稍后重试', type: ToastType.error);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsLogoutFailed,
|
||||
type: ToastType.error,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!mounted) return;
|
||||
@@ -713,28 +711,32 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (!mounted) return;
|
||||
|
||||
if (!result.hasUpdate) {
|
||||
Toast.show(context, '当前已是最新版本', type: ToastType.success);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsLatestVersion,
|
||||
type: ToastType.success,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final message = result.updateType == 'required'
|
||||
? '有新版本可用 (${result.latestVersionName}),请立即更新'
|
||||
: '发现新版本 (${result.latestVersionName}),是否更新?';
|
||||
? context.l10n.settingsUpdateRequired(result.latestVersionName)
|
||||
: context.l10n.settingsUpdateOptional(result.latestVersionName);
|
||||
|
||||
final shouldUpdate = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('检查更新'),
|
||||
title: Text(context.l10n.settingsUpdateDialogTitle),
|
||||
content: Text(message),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('取消'),
|
||||
child: Text(context.l10n.commonCancel),
|
||||
),
|
||||
if (result.downloadUrl != null)
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text('更新'),
|
||||
child: Text(context.l10n.settingsUpdateAction),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -743,13 +745,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
if (shouldUpdate == true && result.downloadUrl != null && mounted) {
|
||||
Toast.show(
|
||||
context,
|
||||
'下载链接: ${result.downloadUrl}',
|
||||
context.l10n.settingsDownloadLink(result.downloadUrl!),
|
||||
type: ToastType.info,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
Toast.show(context, '检查更新失败', type: ToastType.error);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsUpdateCheckFailed,
|
||||
type: ToastType.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,7 +765,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
height: 52,
|
||||
child: AppButton(
|
||||
key: settingsLogoutButtonKey,
|
||||
text: '退出登录',
|
||||
text: context.l10n.settingsLogoutTitle,
|
||||
isOutlined: true,
|
||||
onPressed: () => _onTapLogout(),
|
||||
),
|
||||
+73
-53
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:social_app/core/di/injection.dart';
|
||||
import 'package:social_app/app/di/injection.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
import 'package:social_app/core/theme/design_tokens.dart';
|
||||
import 'package:social_app/shared/widgets/app_loading_indicator.dart';
|
||||
import 'package:social_app/shared/widgets/app_pressable.dart';
|
||||
@@ -48,7 +49,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_error = '加载失败';
|
||||
_error = L10n.current.memoryLoadFailedRetry;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
@@ -69,13 +70,21 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
_isSaving = false;
|
||||
_hasChanges = false;
|
||||
});
|
||||
Toast.show(context, '保存成功', type: ToastType.success);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsMemorySaveSuccess,
|
||||
type: ToastType.success,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isSaving = false;
|
||||
});
|
||||
Toast.show(context, '保存失败', type: ToastType.error);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsMemorySaveFailed,
|
||||
type: ToastType.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +98,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SettingsPageScaffold(
|
||||
title: '编辑个人偏好',
|
||||
title: context.l10n.settingsUserMemoryEditTitle,
|
||||
onBack: () => context.pop(),
|
||||
footer: _hasChanges ? _buildSaveButton() : null,
|
||||
body: Column(
|
||||
@@ -123,7 +132,10 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 48, color: AppColors.slate300),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text(_error ?? '加载失败', style: TextStyle(color: AppColors.slate500)),
|
||||
Text(
|
||||
_error ?? context.l10n.memoryLoadFailedRetry,
|
||||
style: TextStyle(color: AppColors.slate500),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
AppPressable(
|
||||
onTap: _loadMemory,
|
||||
@@ -138,8 +150,8 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
border: Border.all(color: AppColors.blue100),
|
||||
),
|
||||
child: const Text(
|
||||
'重新加载',
|
||||
child: Text(
|
||||
context.l10n.memoryReload,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -160,7 +172,10 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
children: [
|
||||
Icon(Icons.person_off_outlined, size: 48, color: AppColors.slate300),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text('暂无个人偏好信息', style: TextStyle(color: AppColors.slate500)),
|
||||
Text(
|
||||
context.l10n.settingsUserMemoryEmptyProfile,
|
||||
style: TextStyle(color: AppColors.slate500),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -191,8 +206,8 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
child: Center(
|
||||
child: _isSaving
|
||||
? const AppLoadingIndicator(variant: AppLoadingVariant.button)
|
||||
: const Text(
|
||||
'保存更改',
|
||||
: Text(
|
||||
context.l10n.todoSaveChanges,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -229,23 +244,23 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
|
||||
Widget _buildBasicInfoSection() {
|
||||
return _buildSection(
|
||||
title: '基本信息',
|
||||
title: context.l10n.settingsUserMemorySectionBasic,
|
||||
icon: Icons.person_outline,
|
||||
children: [
|
||||
_buildEditField(
|
||||
label: '职业',
|
||||
label: context.l10n.settingsUserMemoryFieldOccupation,
|
||||
value: _memory?.occupation,
|
||||
onChanged: (value) =>
|
||||
_updateMemory(_memory!.copyWith(occupation: value)),
|
||||
),
|
||||
_buildEditField(
|
||||
label: '时区',
|
||||
label: context.l10n.settingsUserMemoryFieldTimezone,
|
||||
value: _memory?.timezone,
|
||||
onChanged: (value) =>
|
||||
_updateMemory(_memory!.copyWith(timezone: value)),
|
||||
),
|
||||
_buildEditField(
|
||||
label: '主要语言',
|
||||
label: context.l10n.settingsUserMemoryFieldPrimaryLanguage,
|
||||
value: _memory?.primaryLanguage,
|
||||
onChanged: (value) =>
|
||||
_updateMemory(_memory!.copyWith(primaryLanguage: value)),
|
||||
@@ -256,12 +271,12 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
|
||||
Widget _buildPeopleSection() {
|
||||
return _buildSection(
|
||||
title: '联系人',
|
||||
title: context.l10n.settingsUserMemorySectionContacts,
|
||||
icon: Icons.people_outline,
|
||||
count: _memory?.people.length ?? 0,
|
||||
children: [
|
||||
if (_memory?.people.isEmpty ?? true)
|
||||
_buildEmptySection('暂无联系人')
|
||||
_buildEmptySection(context.l10n.settingsUserMemoryEmptyContacts)
|
||||
else
|
||||
..._memory!.people.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
@@ -269,9 +284,9 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
return _buildPersonItem(person, index);
|
||||
}),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildAddButton('添加联系人', () {
|
||||
_buildAddButton(context.l10n.settingsUserMemoryAddContact, () {
|
||||
final newPeople = List<Person>.from(_memory!.people)
|
||||
..add(Person(name: '新联系人'));
|
||||
..add(Person(name: context.l10n.settingsUserMemoryNewContact));
|
||||
_updateMemory(_memory!.copyWith(people: newPeople));
|
||||
}),
|
||||
],
|
||||
@@ -294,7 +309,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '姓名',
|
||||
label: context.l10n.settingsUserMemoryFieldName,
|
||||
value: person.name,
|
||||
onChanged: (value) {
|
||||
final newPeople = List<Person>.from(_memory!.people);
|
||||
@@ -322,7 +337,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '关系',
|
||||
label: context.l10n.settingsUserMemoryFieldRelationship,
|
||||
value: person.relationship,
|
||||
onChanged: (value) {
|
||||
final newPeople = List<Person>.from(_memory!.people);
|
||||
@@ -334,7 +349,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '角色',
|
||||
label: context.l10n.settingsUserMemoryFieldRole,
|
||||
value: person.role,
|
||||
onChanged: (value) {
|
||||
final newPeople = List<Person>.from(_memory!.people);
|
||||
@@ -347,7 +362,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildEditField(
|
||||
label: '联系方式',
|
||||
label: context.l10n.settingsUserMemoryFieldContact,
|
||||
value: person.preferredContactChannel,
|
||||
onChanged: (value) {
|
||||
final newPeople = List<Person>.from(_memory!.people);
|
||||
@@ -359,7 +374,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildEditField(
|
||||
label: '备注',
|
||||
label: context.l10n.settingsUserMemoryFieldNotes,
|
||||
value: person.notes,
|
||||
onChanged: (value) {
|
||||
final newPeople = List<Person>.from(_memory!.people);
|
||||
@@ -374,12 +389,12 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
|
||||
Widget _buildPlacesSection() {
|
||||
return _buildSection(
|
||||
title: '地点',
|
||||
title: context.l10n.settingsUserMemorySectionPlaces,
|
||||
icon: Icons.place_outlined,
|
||||
count: _memory?.places.length ?? 0,
|
||||
children: [
|
||||
if (_memory?.places.isEmpty ?? true)
|
||||
_buildEmptySection('暂无地点')
|
||||
_buildEmptySection(context.l10n.settingsUserMemoryEmptyPlaces)
|
||||
else
|
||||
..._memory!.places.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
@@ -387,9 +402,9 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
return _buildPlaceItem(place, index);
|
||||
}),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildAddButton('添加地点', () {
|
||||
_buildAddButton(context.l10n.settingsUserMemoryAddPlace, () {
|
||||
final newPlaces = List<Place>.from(_memory!.places)
|
||||
..add(Place(name: '新地点'));
|
||||
..add(Place(name: context.l10n.settingsUserMemoryNewPlace));
|
||||
_updateMemory(_memory!.copyWith(places: newPlaces));
|
||||
}),
|
||||
],
|
||||
@@ -412,7 +427,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '名称',
|
||||
label: context.l10n.settingsUserMemoryFieldName,
|
||||
value: place.name,
|
||||
onChanged: (value) {
|
||||
final newPlaces = List<Place>.from(_memory!.places);
|
||||
@@ -440,7 +455,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '类别',
|
||||
label: context.l10n.settingsUserMemoryFieldCategory,
|
||||
value: place.category,
|
||||
onChanged: (value) {
|
||||
final newPlaces = List<Place>.from(_memory!.places);
|
||||
@@ -452,7 +467,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '偏好',
|
||||
label: context.l10n.settingsUserMemoryFieldPreference,
|
||||
value: place.preference,
|
||||
onChanged: (value) {
|
||||
final newPlaces = List<Place>.from(_memory!.places);
|
||||
@@ -465,7 +480,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildEditField(
|
||||
label: '地址',
|
||||
label: context.l10n.settingsUserMemoryFieldAddress,
|
||||
value: place.address,
|
||||
onChanged: (value) {
|
||||
final newPlaces = List<Place>.from(_memory!.places);
|
||||
@@ -481,11 +496,11 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
Widget _buildPreferencesSection() {
|
||||
final prefs = _memory!.preferences;
|
||||
return _buildSection(
|
||||
title: '偏好设置',
|
||||
title: context.l10n.settingsUserMemorySectionPreferences,
|
||||
icon: Icons.settings_outlined,
|
||||
children: [
|
||||
_buildEditField(
|
||||
label: '沟通风格',
|
||||
label: context.l10n.settingsUserMemoryFieldCommunicationStyle,
|
||||
value: prefs.communicationStyle,
|
||||
onChanged: (value) {
|
||||
_updateMemory(
|
||||
@@ -496,7 +511,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
},
|
||||
),
|
||||
_buildEditField(
|
||||
label: '位置偏好',
|
||||
label: context.l10n.settingsUserMemoryFieldLocationPreference,
|
||||
value: prefs.locationPreference,
|
||||
onChanged: (value) {
|
||||
_updateMemory(
|
||||
@@ -507,7 +522,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
},
|
||||
),
|
||||
_buildEditField(
|
||||
label: '工作生活方式',
|
||||
label: context.l10n.settingsUserMemoryFieldWorkLifestyle,
|
||||
value: prefs.workLifestyle,
|
||||
onChanged: (value) {
|
||||
_updateMemory(
|
||||
@@ -523,7 +538,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
|
||||
Widget _buildInterestsSection() {
|
||||
return _buildSection(
|
||||
title: '兴趣',
|
||||
title: context.l10n.settingsUserMemorySectionInterests,
|
||||
icon: Icons.interests_outlined,
|
||||
children: [
|
||||
_buildTagsSection(
|
||||
@@ -545,7 +560,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
|
||||
Widget _buildAvoidTopicsSection() {
|
||||
return _buildSection(
|
||||
title: '回避话题',
|
||||
title: context.l10n.settingsUserMemorySectionAvoidTopics,
|
||||
icon: Icons.not_interested_outlined,
|
||||
children: [
|
||||
_buildTagsSection(
|
||||
@@ -567,12 +582,12 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
|
||||
Widget _buildRecurringRoutinesSection() {
|
||||
return _buildSection(
|
||||
title: '周期习惯',
|
||||
title: context.l10n.settingsUserMemorySectionRoutines,
|
||||
icon: Icons.schedule_outlined,
|
||||
count: _memory?.recurringRoutines.length ?? 0,
|
||||
children: [
|
||||
if (_memory?.recurringRoutines.isEmpty ?? true)
|
||||
_buildEmptySection('暂无周期习惯')
|
||||
_buildEmptySection(context.l10n.settingsUserMemoryEmptyRoutines)
|
||||
else
|
||||
..._memory!.recurringRoutines.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
@@ -580,10 +595,13 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
return _buildRoutineItem(routine, index);
|
||||
}),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildAddButton('添加习惯', () {
|
||||
final newRoutines = List<RecurringRoutine>.from(
|
||||
_memory!.recurringRoutines,
|
||||
)..add(RecurringRoutine(name: '新习惯'));
|
||||
_buildAddButton(context.l10n.settingsUserMemoryAddRoutine, () {
|
||||
final newRoutines =
|
||||
List<RecurringRoutine>.from(_memory!.recurringRoutines)..add(
|
||||
RecurringRoutine(
|
||||
name: context.l10n.settingsUserMemoryNewRoutine,
|
||||
),
|
||||
);
|
||||
_updateMemory(_memory!.copyWith(recurringRoutines: newRoutines));
|
||||
}),
|
||||
],
|
||||
@@ -606,7 +624,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '名称',
|
||||
label: context.l10n.settingsUserMemoryFieldName,
|
||||
value: routine.name,
|
||||
onChanged: (value) {
|
||||
final newRoutines = List<RecurringRoutine>.from(
|
||||
@@ -641,7 +659,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '描述',
|
||||
label: context.l10n.settingsUserMemoryFieldDescription,
|
||||
value: routine.description,
|
||||
onChanged: (value) {
|
||||
final newRoutines = List<RecurringRoutine>.from(
|
||||
@@ -657,7 +675,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '周期',
|
||||
label: context.l10n.settingsUserMemoryFieldCadence,
|
||||
value: routine.cadence,
|
||||
onChanged: (value) {
|
||||
final newRoutines = List<RecurringRoutine>.from(
|
||||
@@ -760,7 +778,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
hintText: '输入$label',
|
||||
hintText: context.l10n.settingsMemoryInputHint(label),
|
||||
hintStyle: TextStyle(color: AppColors.slate400, fontSize: 14),
|
||||
),
|
||||
),
|
||||
@@ -855,7 +873,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
Icon(Icons.add, size: 14, color: AppColors.slate500),
|
||||
const SizedBox(width: AppSpacing.xs),
|
||||
Text(
|
||||
'添加',
|
||||
context.l10n.contactsAdd,
|
||||
style: TextStyle(fontSize: 13, color: AppColors.slate500),
|
||||
),
|
||||
],
|
||||
@@ -873,16 +891,18 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('添加'),
|
||||
title: Text(context.l10n.contactsAdd),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(hintText: '输入内容'),
|
||||
decoration: InputDecoration(
|
||||
hintText: context.l10n.settingsMemoryInputContent,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('取消'),
|
||||
child: Text(context.l10n.commonCancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
@@ -891,7 +911,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
|
||||
}
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('添加'),
|
||||
child: Text(context.l10n.contactsAdd),
|
||||
),
|
||||
],
|
||||
),
|
||||
+86
-53
@@ -1,7 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:social_app/core/di/injection.dart';
|
||||
import 'package:social_app/core/router/app_routes.dart';
|
||||
import 'package:social_app/app/di/injection.dart';
|
||||
import 'package:social_app/app/router/app_routes.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
import 'package:social_app/core/theme/design_tokens.dart';
|
||||
import 'package:social_app/shared/widgets/app_loading_indicator.dart';
|
||||
import 'package:social_app/shared/widgets/app_pressable.dart';
|
||||
@@ -48,7 +49,7 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
} catch (_) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_error = '加载失败';
|
||||
_error = L10n.current.memoryLoadFailedRetry;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
@@ -64,13 +65,13 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SettingsPageScaffold(
|
||||
title: '个人偏好',
|
||||
title: context.l10n.memoryUserProfile,
|
||||
onBack: () => context.pop(),
|
||||
trailing: DetailHeaderActionMenu<_UserMemoryHeaderAction>(
|
||||
items: const [
|
||||
items: [
|
||||
DetailHeaderActionItem<_UserMemoryHeaderAction>(
|
||||
value: _UserMemoryHeaderAction.edit,
|
||||
label: '编辑',
|
||||
label: context.l10n.commonEdit,
|
||||
icon: Icons.edit_outlined,
|
||||
),
|
||||
],
|
||||
@@ -100,7 +101,10 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 48, color: AppColors.slate300),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text(_error ?? '加载失败', style: TextStyle(color: AppColors.slate500)),
|
||||
Text(
|
||||
_error ?? context.l10n.memoryLoadFailedRetry,
|
||||
style: TextStyle(color: AppColors.slate500),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
AppPressable(
|
||||
onTap: _loadMemory,
|
||||
@@ -115,8 +119,8 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
border: Border.all(color: AppColors.blue100),
|
||||
),
|
||||
child: const Text(
|
||||
'重新加载',
|
||||
child: Text(
|
||||
context.l10n.memoryReload,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -131,80 +135,89 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
}
|
||||
|
||||
Widget _buildContent(UserMemoryContent memory) {
|
||||
final l10n = context.l10n;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildSectionCard(
|
||||
title: '基本信息',
|
||||
title: l10n.settingsUserMemorySectionBasic,
|
||||
icon: Icons.person_outline,
|
||||
children: [
|
||||
_buildInfoRow(Icons.work_outline, '职业', _text(memory.occupation)),
|
||||
_buildInfoRow(Icons.public_outlined, '时区', _text(memory.timezone)),
|
||||
_buildInfoRow(
|
||||
Icons.work_outline,
|
||||
l10n.settingsUserMemoryFieldOccupation,
|
||||
_text(memory.occupation),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.public_outlined,
|
||||
l10n.settingsUserMemoryFieldTimezone,
|
||||
_text(memory.timezone),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.translate_outlined,
|
||||
'主要语言',
|
||||
l10n.settingsUserMemoryFieldPrimaryLanguage,
|
||||
_text(memory.primaryLanguage),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '偏好设置',
|
||||
title: l10n.settingsUserMemorySectionPreferences,
|
||||
icon: Icons.tune,
|
||||
children: [
|
||||
_buildInfoRow(
|
||||
Icons.chat_bubble_outline,
|
||||
'沟通风格',
|
||||
l10n.settingsUserMemoryFieldCommunicationStyle,
|
||||
_text(memory.preferences.communicationStyle),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.place_outlined,
|
||||
'位置偏好',
|
||||
l10n.settingsUserMemoryFieldLocationPreference,
|
||||
_text(memory.preferences.locationPreference),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.balcony_outlined,
|
||||
'工作生活方式',
|
||||
l10n.settingsUserMemoryFieldWorkLifestyle,
|
||||
_text(memory.preferences.workLifestyle),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.language_outlined,
|
||||
'语言偏好',
|
||||
l10n.settingsUserMemoryFieldLanguagePreference,
|
||||
_listText(memory.preferences.languagePreference),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.notifications_outlined,
|
||||
'通知偏好',
|
||||
l10n.settingsUserMemoryFieldNotificationPreference,
|
||||
_listText(memory.preferences.notificationPreference),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '日程偏好',
|
||||
title: l10n.settingsUserMemorySectionSchedule,
|
||||
icon: Icons.schedule_outlined,
|
||||
children: [
|
||||
_buildInfoRow(
|
||||
Icons.timer_outlined,
|
||||
'会议缓冲时间',
|
||||
l10n.settingsUserMemoryFieldMeetingBuffer,
|
||||
_minutesText(memory.schedulingPreferences.meetingBufferMinutes),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.format_list_numbered,
|
||||
'每日最多会议',
|
||||
l10n.settingsUserMemoryFieldMaxMeetingsPerDay,
|
||||
_numberText(memory.schedulingPreferences.maxMeetingsPerDay),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.timelapse_outlined,
|
||||
'偏好会议时长',
|
||||
l10n.settingsUserMemoryFieldPreferredMeetingDuration,
|
||||
_intListText(
|
||||
memory.schedulingPreferences.preferredMeetingDurationMinutes,
|
||||
suffix: '分钟',
|
||||
suffix: l10n.settingsUserMemoryMinute,
|
||||
),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.note_outlined,
|
||||
'备注',
|
||||
l10n.settingsUserMemoryFieldNotes,
|
||||
_text(memory.schedulingPreferences.notes),
|
||||
multiline: true,
|
||||
),
|
||||
@@ -212,37 +225,37 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '联系人',
|
||||
title: l10n.settingsUserMemorySectionContacts,
|
||||
icon: Icons.people_outline,
|
||||
children: [_buildPeople(memory.people)],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '地点',
|
||||
title: l10n.settingsUserMemorySectionPlaces,
|
||||
icon: Icons.place_outlined,
|
||||
children: [_buildPlaces(memory.places)],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '兴趣',
|
||||
title: l10n.settingsUserMemorySectionInterests,
|
||||
icon: Icons.interests_outlined,
|
||||
children: [_buildTags(memory.interests)],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '回避话题',
|
||||
title: l10n.settingsUserMemorySectionAvoidTopics,
|
||||
icon: Icons.not_interested_outlined,
|
||||
children: [_buildTags(memory.avoidTopics)],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '自定义规则',
|
||||
title: l10n.settingsUserMemorySectionCustomRules,
|
||||
icon: Icons.rule_folder_outlined,
|
||||
children: [_buildTags(memory.customRules)],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '周期习惯',
|
||||
title: l10n.settingsUserMemorySectionRoutines,
|
||||
icon: Icons.repeat,
|
||||
children: [_buildRoutines(memory.recurringRoutines)],
|
||||
),
|
||||
@@ -347,7 +360,7 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
|
||||
Widget _buildPeople(List<Person> people) {
|
||||
if (people.isEmpty) {
|
||||
return _buildEmptyTip('暂无联系人');
|
||||
return _buildEmptyTip(context.l10n.settingsUserMemoryEmptyContacts);
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -363,25 +376,29 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildInfoRow(Icons.badge_outlined, '姓名', _text(person.name)),
|
||||
_buildInfoRow(
|
||||
Icons.badge_outlined,
|
||||
context.l10n.settingsUserMemoryFieldName,
|
||||
_text(person.name),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.account_tree_outlined,
|
||||
'关系',
|
||||
context.l10n.settingsUserMemoryFieldRelationship,
|
||||
_text(person.relationship),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.person_pin_outlined,
|
||||
'角色',
|
||||
context.l10n.settingsUserMemoryFieldRole,
|
||||
_text(person.role),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.phone_outlined,
|
||||
'联系方式',
|
||||
context.l10n.settingsUserMemoryFieldContact,
|
||||
_text(person.preferredContactChannel),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.note_outlined,
|
||||
'备注',
|
||||
context.l10n.settingsUserMemoryFieldNotes,
|
||||
_text(person.notes),
|
||||
multiline: true,
|
||||
),
|
||||
@@ -394,7 +411,7 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
|
||||
Widget _buildPlaces(List<Place> places) {
|
||||
if (places.isEmpty) {
|
||||
return _buildEmptyTip('暂无地点');
|
||||
return _buildEmptyTip(context.l10n.settingsUserMemoryEmptyPlaces);
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -410,18 +427,26 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildInfoRow(Icons.place_outlined, '名称', _text(place.name)),
|
||||
_buildInfoRow(
|
||||
Icons.place_outlined,
|
||||
context.l10n.settingsUserMemoryFieldName,
|
||||
_text(place.name),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.category_outlined,
|
||||
'类别',
|
||||
context.l10n.settingsUserMemoryFieldCategory,
|
||||
_text(place.category),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.favorite_border,
|
||||
'偏好',
|
||||
context.l10n.settingsUserMemoryFieldPreference,
|
||||
_text(place.preference),
|
||||
),
|
||||
_buildInfoRow(Icons.map_outlined, '地址', _text(place.address)),
|
||||
_buildInfoRow(
|
||||
Icons.map_outlined,
|
||||
context.l10n.settingsUserMemoryFieldAddress,
|
||||
_text(place.address),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -431,7 +456,7 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
|
||||
Widget _buildRoutines(List<RecurringRoutine> routines) {
|
||||
if (routines.isEmpty) {
|
||||
return _buildEmptyTip('暂无周期习惯');
|
||||
return _buildEmptyTip(context.l10n.settingsUserMemoryEmptyRoutines);
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -447,14 +472,22 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildInfoRow(Icons.title_outlined, '名称', _text(routine.name)),
|
||||
_buildInfoRow(
|
||||
Icons.title_outlined,
|
||||
context.l10n.settingsUserMemoryFieldName,
|
||||
_text(routine.name),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.subject_outlined,
|
||||
'描述',
|
||||
context.l10n.settingsUserMemoryFieldDescription,
|
||||
_text(routine.description),
|
||||
multiline: true,
|
||||
),
|
||||
_buildInfoRow(Icons.repeat_one, '周期', _text(routine.cadence)),
|
||||
_buildInfoRow(
|
||||
Icons.repeat_one,
|
||||
context.l10n.settingsUserMemoryFieldCadence,
|
||||
_text(routine.cadence),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -464,7 +497,7 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
|
||||
Widget _buildTags(List<String> tags) {
|
||||
if (tags.isEmpty) {
|
||||
return _buildEmptyTip('暂无数据');
|
||||
return _buildEmptyTip(context.l10n.memoryNoInfo);
|
||||
}
|
||||
return Wrap(
|
||||
spacing: AppSpacing.sm,
|
||||
@@ -529,26 +562,26 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
|
||||
|
||||
String _text(String? value) {
|
||||
final raw = value?.trim() ?? '';
|
||||
return raw.isEmpty ? '未设置' : raw;
|
||||
return raw.isEmpty ? context.l10n.settingsUnset : raw;
|
||||
}
|
||||
|
||||
String _listText(List<String> values) {
|
||||
if (values.isEmpty) return '未设置';
|
||||
if (values.isEmpty) return context.l10n.settingsUnset;
|
||||
return values.join('、');
|
||||
}
|
||||
|
||||
String _intListText(List<int> values, {required String suffix}) {
|
||||
if (values.isEmpty) return '未设置';
|
||||
if (values.isEmpty) return context.l10n.settingsUnset;
|
||||
return values.map((value) => '$value$suffix').join('、');
|
||||
}
|
||||
|
||||
String _minutesText(int? value) {
|
||||
if (value == null) return '未设置';
|
||||
return '$value 分钟';
|
||||
if (value == null) return context.l10n.settingsUnset;
|
||||
return context.l10n.settingsUserMemoryMinutesValue(value);
|
||||
}
|
||||
|
||||
String _numberText(int? value) {
|
||||
if (value == null) return '未设置';
|
||||
if (value == null) return context.l10n.settingsUnset;
|
||||
return '$value';
|
||||
}
|
||||
}
|
||||
+65
-47
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:social_app/core/di/injection.dart';
|
||||
import 'package:social_app/app/di/injection.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
import 'package:social_app/core/theme/design_tokens.dart';
|
||||
import 'package:social_app/shared/widgets/app_loading_indicator.dart';
|
||||
import 'package:social_app/shared/widgets/app_pressable.dart';
|
||||
@@ -48,7 +49,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_error = '加载失败';
|
||||
_error = L10n.current.memoryLoadFailedRetry;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
@@ -69,13 +70,21 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
_isSaving = false;
|
||||
_hasChanges = false;
|
||||
});
|
||||
Toast.show(context, '保存成功', type: ToastType.success);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsMemorySaveSuccess,
|
||||
type: ToastType.success,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isSaving = false;
|
||||
});
|
||||
Toast.show(context, '保存失败', type: ToastType.error);
|
||||
Toast.show(
|
||||
context,
|
||||
context.l10n.settingsMemorySaveFailed,
|
||||
type: ToastType.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +98,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SettingsPageScaffold(
|
||||
title: '编辑工作画像',
|
||||
title: context.l10n.settingsWorkMemoryEditTitle,
|
||||
onBack: () => context.pop(),
|
||||
footer: _hasChanges ? _buildSaveButton() : null,
|
||||
body: Column(
|
||||
@@ -123,7 +132,10 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 48, color: AppColors.slate300),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text(_error ?? '加载失败', style: TextStyle(color: AppColors.slate500)),
|
||||
Text(
|
||||
_error ?? context.l10n.memoryLoadFailedRetry,
|
||||
style: TextStyle(color: AppColors.slate500),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
AppPressable(
|
||||
onTap: _loadMemory,
|
||||
@@ -138,8 +150,8 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
border: Border.all(color: AppColors.blue100),
|
||||
),
|
||||
child: const Text(
|
||||
'重新加载',
|
||||
child: Text(
|
||||
context.l10n.memoryReload,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -160,7 +172,10 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
children: [
|
||||
Icon(Icons.work_off_outlined, size: 48, color: AppColors.slate300),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text('暂无工作信息', style: TextStyle(color: AppColors.slate500)),
|
||||
Text(
|
||||
context.l10n.settingsWorkMemoryEmptyProfile,
|
||||
style: TextStyle(color: AppColors.slate500),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -191,8 +206,8 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
child: Center(
|
||||
child: _isSaving
|
||||
? const AppLoadingIndicator(variant: AppLoadingVariant.button)
|
||||
: const Text(
|
||||
'保存更改',
|
||||
: Text(
|
||||
context.l10n.todoSaveChanges,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -231,11 +246,11 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
|
||||
Widget _buildBasicInfoSection() {
|
||||
return _buildSection(
|
||||
title: '基本信息',
|
||||
title: context.l10n.settingsWorkMemorySectionBasic,
|
||||
icon: Icons.work_outline,
|
||||
children: [
|
||||
_buildEditField(
|
||||
label: '职业',
|
||||
label: context.l10n.settingsWorkMemoryFieldOccupation,
|
||||
value: _memory?.occupation,
|
||||
onChanged: (value) =>
|
||||
_updateMemory(_memory!.copyWith(occupation: value)),
|
||||
@@ -246,7 +261,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
|
||||
Widget _buildExpertiseSection() {
|
||||
return _buildSection(
|
||||
title: '专长',
|
||||
title: context.l10n.settingsWorkMemorySectionExpertise,
|
||||
icon: Icons.psychology_outlined,
|
||||
count: _memory?.expertise.length ?? 0,
|
||||
children: [
|
||||
@@ -269,7 +284,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
|
||||
Widget _buildPreferredToolsSection() {
|
||||
return _buildSection(
|
||||
title: '偏好工具',
|
||||
title: context.l10n.settingsWorkMemorySectionPreferredTools,
|
||||
icon: Icons.build_outlined,
|
||||
count: _memory?.preferredTools.length ?? 0,
|
||||
children: [
|
||||
@@ -294,12 +309,12 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
|
||||
Widget _buildProjectsSection() {
|
||||
return _buildSection(
|
||||
title: '当前项目',
|
||||
title: context.l10n.settingsWorkMemorySectionCurrentProjects,
|
||||
icon: Icons.folder_outlined,
|
||||
count: _memory?.currentProjects.length ?? 0,
|
||||
children: [
|
||||
if (_memory?.currentProjects.isEmpty ?? true)
|
||||
_buildEmptySection('暂无项目')
|
||||
_buildEmptySection(context.l10n.settingsWorkMemoryEmptyProjects)
|
||||
else
|
||||
..._memory!.currentProjects.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
@@ -307,10 +322,11 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
return _buildProjectItem(project, index);
|
||||
}),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildAddButton('添加项目', () {
|
||||
final newProjects = List<CurrentProject>.from(
|
||||
_memory!.currentProjects,
|
||||
)..add(CurrentProject(name: '新项目'));
|
||||
_buildAddButton(context.l10n.settingsWorkMemoryAddProject, () {
|
||||
final newProjects =
|
||||
List<CurrentProject>.from(_memory!.currentProjects)..add(
|
||||
CurrentProject(name: context.l10n.settingsWorkMemoryNewProject),
|
||||
);
|
||||
_updateMemory(_memory!.copyWith(currentProjects: newProjects));
|
||||
}),
|
||||
],
|
||||
@@ -333,7 +349,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '项目名称',
|
||||
label: context.l10n.settingsWorkMemoryFieldProjectName,
|
||||
value: project.name,
|
||||
onChanged: (value) {
|
||||
final newProjects = List<CurrentProject>.from(
|
||||
@@ -368,7 +384,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '状态',
|
||||
label: context.l10n.settingsWorkMemoryFieldStatus,
|
||||
value: project.status,
|
||||
onChanged: (value) {
|
||||
final newProjects = List<CurrentProject>.from(
|
||||
@@ -384,7 +400,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '优先级',
|
||||
label: context.l10n.settingsWorkMemoryFieldPriority,
|
||||
value: project.priority,
|
||||
onChanged: (value) {
|
||||
final newProjects = List<CurrentProject>.from(
|
||||
@@ -401,7 +417,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildEditField(
|
||||
label: '描述',
|
||||
label: context.l10n.settingsUserMemoryFieldDescription,
|
||||
value: project.description,
|
||||
onChanged: (value) {
|
||||
final newProjects = List<CurrentProject>.from(
|
||||
@@ -413,7 +429,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildEditField(
|
||||
label: '截止日期',
|
||||
label: context.l10n.settingsWorkMemoryFieldDeadline,
|
||||
value: project.deadline?.toIso8601String().split('T').first,
|
||||
onChanged: (value) {
|
||||
final newProjects = List<CurrentProject>.from(
|
||||
@@ -432,12 +448,12 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
|
||||
Widget _buildTeamMembersSection() {
|
||||
return _buildSection(
|
||||
title: '团队成员',
|
||||
title: context.l10n.settingsWorkMemorySectionTeamMembers,
|
||||
icon: Icons.groups_outlined,
|
||||
count: _memory?.teamMembers.length ?? 0,
|
||||
children: [
|
||||
if (_memory?.teamMembers.isEmpty ?? true)
|
||||
_buildEmptySection('暂无团队成员')
|
||||
_buildEmptySection(context.l10n.settingsWorkMemoryEmptyTeamMembers)
|
||||
else
|
||||
..._memory!.teamMembers.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
@@ -445,9 +461,9 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
return _buildTeamMemberItem(member, index);
|
||||
}),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildAddButton('添加成员', () {
|
||||
_buildAddButton(context.l10n.settingsWorkMemoryAddMember, () {
|
||||
final newMembers = List<TeamMember>.from(_memory!.teamMembers)
|
||||
..add(TeamMember(name: '新成员'));
|
||||
..add(TeamMember(name: context.l10n.settingsWorkMemoryNewMember));
|
||||
_updateMemory(_memory!.copyWith(teamMembers: newMembers));
|
||||
}),
|
||||
],
|
||||
@@ -470,7 +486,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '姓名',
|
||||
label: context.l10n.settingsUserMemoryFieldName,
|
||||
value: member.name,
|
||||
onChanged: (value) {
|
||||
final newMembers = List<TeamMember>.from(
|
||||
@@ -500,7 +516,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '角色',
|
||||
label: context.l10n.settingsUserMemoryFieldRole,
|
||||
value: member.role,
|
||||
onChanged: (value) {
|
||||
final newMembers = List<TeamMember>.from(
|
||||
@@ -514,7 +530,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: _buildEditField(
|
||||
label: '关系',
|
||||
label: context.l10n.settingsUserMemoryFieldRelationship,
|
||||
value: member.relationship,
|
||||
onChanged: (value) {
|
||||
final newMembers = List<TeamMember>.from(
|
||||
@@ -529,7 +545,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildEditField(
|
||||
label: '联系方式',
|
||||
label: context.l10n.settingsUserMemoryFieldContact,
|
||||
value: member.preferredContactChannel,
|
||||
onChanged: (value) {
|
||||
final newMembers = List<TeamMember>.from(_memory!.teamMembers);
|
||||
@@ -541,7 +557,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildEditField(
|
||||
label: '备注',
|
||||
label: context.l10n.settingsUserMemoryFieldNotes,
|
||||
value: member.notes,
|
||||
onChanged: (value) {
|
||||
final newMembers = List<TeamMember>.from(_memory!.teamMembers);
|
||||
@@ -557,11 +573,11 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
Widget _buildWorkHabitsSection() {
|
||||
final habits = _memory!.workHabits;
|
||||
return _buildSection(
|
||||
title: '工作习惯',
|
||||
title: context.l10n.settingsWorkMemorySectionWorkHabits,
|
||||
icon: Icons.schedule_outlined,
|
||||
children: [
|
||||
_buildEditField(
|
||||
label: '通知渠道',
|
||||
label: context.l10n.settingsWorkMemoryFieldNotificationChannel,
|
||||
value: habits.notificationChannel,
|
||||
onChanged: (value) {
|
||||
_updateMemory(
|
||||
@@ -573,7 +589,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
_buildEditField(
|
||||
label: '备注',
|
||||
label: context.l10n.settingsWorkMemoryFieldNotes,
|
||||
value: habits.notes,
|
||||
onChanged: (value) {
|
||||
_updateMemory(
|
||||
@@ -587,11 +603,11 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
|
||||
Widget _buildTeamContextSection() {
|
||||
return _buildSection(
|
||||
title: '团队背景',
|
||||
title: context.l10n.settingsWorkMemorySectionTeamContext,
|
||||
icon: Icons.business_outlined,
|
||||
children: [
|
||||
_buildEditField(
|
||||
label: '团队背景描述',
|
||||
label: context.l10n.settingsWorkMemoryFieldTeamContext,
|
||||
value: _memory?.teamContext,
|
||||
onChanged: (value) =>
|
||||
_updateMemory(_memory!.copyWith(teamContext: value)),
|
||||
@@ -602,7 +618,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
|
||||
Widget _buildWorkRulesSection() {
|
||||
return _buildSection(
|
||||
title: '工作规则',
|
||||
title: context.l10n.settingsWorkMemorySectionWorkRules,
|
||||
icon: Icons.rule_outlined,
|
||||
children: [
|
||||
_buildTagsSection(
|
||||
@@ -705,7 +721,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
hintText: '输入$label',
|
||||
hintText: context.l10n.settingsMemoryInputHint(label),
|
||||
hintStyle: TextStyle(color: AppColors.slate400, fontSize: 14),
|
||||
),
|
||||
),
|
||||
@@ -802,7 +818,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
Icon(Icons.add, size: 14, color: AppColors.slate500),
|
||||
const SizedBox(width: AppSpacing.xs),
|
||||
Text(
|
||||
'添加',
|
||||
context.l10n.contactsAdd,
|
||||
style: TextStyle(fontSize: 13, color: AppColors.slate500),
|
||||
),
|
||||
],
|
||||
@@ -820,16 +836,18 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('添加'),
|
||||
title: Text(context.l10n.contactsAdd),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(hintText: '输入内容'),
|
||||
decoration: InputDecoration(
|
||||
hintText: context.l10n.settingsMemoryInputContent,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('取消'),
|
||||
child: Text(context.l10n.commonCancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
@@ -838,7 +856,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
|
||||
}
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('添加'),
|
||||
child: Text(context.l10n.contactsAdd),
|
||||
),
|
||||
],
|
||||
),
|
||||
+78
-51
@@ -1,7 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:social_app/core/di/injection.dart';
|
||||
import 'package:social_app/core/router/app_routes.dart';
|
||||
import 'package:social_app/app/di/injection.dart';
|
||||
import 'package:social_app/app/router/app_routes.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
import 'package:social_app/core/theme/design_tokens.dart';
|
||||
import 'package:social_app/shared/widgets/app_loading_indicator.dart';
|
||||
import 'package:social_app/shared/widgets/app_pressable.dart';
|
||||
@@ -48,7 +49,7 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
} catch (_) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_error = '加载失败';
|
||||
_error = L10n.current.memoryLoadFailedRetry;
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
@@ -64,13 +65,13 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SettingsPageScaffold(
|
||||
title: '工作画像',
|
||||
title: context.l10n.memoryWorkProfile,
|
||||
onBack: () => context.pop(),
|
||||
trailing: DetailHeaderActionMenu<_WorkMemoryHeaderAction>(
|
||||
items: const [
|
||||
items: [
|
||||
DetailHeaderActionItem<_WorkMemoryHeaderAction>(
|
||||
value: _WorkMemoryHeaderAction.edit,
|
||||
label: '编辑',
|
||||
label: context.l10n.commonEdit,
|
||||
icon: Icons.edit_outlined,
|
||||
),
|
||||
],
|
||||
@@ -100,7 +101,10 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 48, color: AppColors.slate300),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text(_error ?? '加载失败', style: TextStyle(color: AppColors.slate500)),
|
||||
Text(
|
||||
_error ?? context.l10n.memoryLoadFailedRetry,
|
||||
style: TextStyle(color: AppColors.slate500),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
AppPressable(
|
||||
onTap: _loadMemory,
|
||||
@@ -115,8 +119,8 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
border: Border.all(color: AppColors.blue100),
|
||||
),
|
||||
child: const Text(
|
||||
'重新加载',
|
||||
child: Text(
|
||||
context.l10n.memoryReload,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -131,81 +135,86 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
}
|
||||
|
||||
Widget _buildContent(WorkProfileContent memory) {
|
||||
final l10n = context.l10n;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildSectionCard(
|
||||
title: '基本信息',
|
||||
title: l10n.settingsWorkMemorySectionBasic,
|
||||
icon: Icons.work_outline,
|
||||
children: [
|
||||
_buildInfoRow(Icons.badge_outlined, '职业', _text(memory.occupation)),
|
||||
_buildInfoRow(
|
||||
Icons.badge_outlined,
|
||||
l10n.settingsWorkMemoryFieldOccupation,
|
||||
_text(memory.occupation),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '专长',
|
||||
title: l10n.settingsWorkMemorySectionExpertise,
|
||||
icon: Icons.psychology_outlined,
|
||||
children: [_buildTags(memory.expertise)],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '偏好工具',
|
||||
title: l10n.settingsWorkMemorySectionPreferredTools,
|
||||
icon: Icons.build_outlined,
|
||||
children: [_buildTags(memory.preferredTools)],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '当前项目',
|
||||
title: l10n.settingsWorkMemorySectionCurrentProjects,
|
||||
icon: Icons.folder_outlined,
|
||||
children: [_buildProjects(memory.currentProjects)],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '团队成员',
|
||||
title: l10n.settingsWorkMemorySectionTeamMembers,
|
||||
icon: Icons.groups_outlined,
|
||||
children: [_buildTeamMembers(memory.teamMembers)],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '工作习惯',
|
||||
title: l10n.settingsWorkMemorySectionWorkHabits,
|
||||
icon: Icons.schedule_outlined,
|
||||
children: [
|
||||
_buildInfoRow(
|
||||
Icons.timelapse_outlined,
|
||||
'可用时段',
|
||||
l10n.settingsWorkMemoryFieldAvailableHours,
|
||||
_timeWindowSummary(memory.workHabits.availableHours),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.flash_on_outlined,
|
||||
'深度工作时段',
|
||||
l10n.settingsWorkMemoryFieldDeepWorkBlocks,
|
||||
_timeWindowSummary(memory.workHabits.deepWorkBlocks),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.meeting_room_outlined,
|
||||
'偏好会议时段',
|
||||
l10n.settingsWorkMemoryFieldPreferredMeetingWindows,
|
||||
_timeWindowSummary(memory.workHabits.preferredMeetingWindows),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.do_not_disturb_alt_outlined,
|
||||
'免打扰时段',
|
||||
l10n.settingsWorkMemoryFieldNoMeetingWindows,
|
||||
_timeWindowSummary(memory.workHabits.noMeetingWindows),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.timer_outlined,
|
||||
'偏好会议时长',
|
||||
l10n.settingsWorkMemoryFieldPreferredMeetingDuration,
|
||||
_intListText(
|
||||
memory.workHabits.preferredMeetingDurationMinutes,
|
||||
suffix: '分钟',
|
||||
suffix: l10n.settingsWorkMemoryMinute,
|
||||
),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.notifications_outlined,
|
||||
'通知渠道',
|
||||
l10n.settingsWorkMemoryFieldNotificationChannel,
|
||||
_text(memory.workHabits.notificationChannel),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.note_outlined,
|
||||
'备注',
|
||||
l10n.settingsWorkMemoryFieldNotes,
|
||||
_text(memory.workHabits.notes),
|
||||
multiline: true,
|
||||
),
|
||||
@@ -213,12 +222,12 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '团队背景',
|
||||
title: l10n.settingsWorkMemorySectionTeamContext,
|
||||
icon: Icons.business_outlined,
|
||||
children: [
|
||||
_buildInfoRow(
|
||||
Icons.apartment_outlined,
|
||||
'团队背景描述',
|
||||
l10n.settingsWorkMemoryFieldTeamContext,
|
||||
_text(memory.teamContext),
|
||||
multiline: true,
|
||||
),
|
||||
@@ -226,7 +235,7 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildSectionCard(
|
||||
title: '工作规则',
|
||||
title: l10n.settingsWorkMemorySectionWorkRules,
|
||||
icon: Icons.rule_outlined,
|
||||
children: [_buildTags(memory.workRules)],
|
||||
),
|
||||
@@ -331,7 +340,7 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
|
||||
Widget _buildProjects(List<CurrentProject> projects) {
|
||||
if (projects.isEmpty) {
|
||||
return _buildEmptyTip('暂无项目');
|
||||
return _buildEmptyTip(context.l10n.settingsWorkMemoryEmptyProjects);
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -347,41 +356,51 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildInfoRow(Icons.title_outlined, '项目名称', _text(project.name)),
|
||||
_buildInfoRow(
|
||||
Icons.title_outlined,
|
||||
context.l10n.settingsWorkMemoryFieldProjectName,
|
||||
_text(project.name),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.subject_outlined,
|
||||
'描述',
|
||||
context.l10n.settingsUserMemoryFieldDescription,
|
||||
_text(project.description),
|
||||
multiline: true,
|
||||
),
|
||||
_buildInfoRow(Icons.flag_outlined, '状态', _text(project.status)),
|
||||
_buildInfoRow(
|
||||
Icons.flag_outlined,
|
||||
context.l10n.settingsWorkMemoryFieldStatus,
|
||||
_text(project.status),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.priority_high_outlined,
|
||||
'优先级',
|
||||
context.l10n.settingsWorkMemoryFieldPriority,
|
||||
_text(project.priority),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.event_outlined,
|
||||
'截止日期',
|
||||
context.l10n.settingsWorkMemoryFieldDeadline,
|
||||
project.deadline == null
|
||||
? '未设置'
|
||||
? context.l10n.settingsUnset
|
||||
: project.deadline!.toIso8601String().split('T').first,
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.group_add_outlined,
|
||||
'协作人',
|
||||
context.l10n.settingsWorkMemoryFieldCollaborators,
|
||||
_listText(project.collaborators),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.emoji_events_outlined,
|
||||
'关键里程碑',
|
||||
context.l10n.settingsWorkMemoryFieldMilestones,
|
||||
project.keyMilestones.isEmpty
|
||||
? '未设置'
|
||||
: '${project.keyMilestones.length} 项',
|
||||
? context.l10n.settingsUnset
|
||||
: context.l10n.settingsWorkMemoryMilestoneCount(
|
||||
project.keyMilestones.length,
|
||||
),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.note_alt_outlined,
|
||||
'备注',
|
||||
context.l10n.settingsWorkMemoryFieldNotes,
|
||||
_text(project.notes),
|
||||
),
|
||||
],
|
||||
@@ -393,7 +412,7 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
|
||||
Widget _buildTeamMembers(List<TeamMember> members) {
|
||||
if (members.isEmpty) {
|
||||
return _buildEmptyTip('暂无团队成员');
|
||||
return _buildEmptyTip(context.l10n.settingsWorkMemoryEmptyTeamMembers);
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -409,23 +428,31 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildInfoRow(Icons.badge_outlined, '姓名', _text(member.name)),
|
||||
_buildInfoRow(
|
||||
Icons.badge_outlined,
|
||||
context.l10n.settingsUserMemoryFieldName,
|
||||
_text(member.name),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.person_pin_outlined,
|
||||
'角色',
|
||||
context.l10n.settingsUserMemoryFieldRole,
|
||||
_text(member.role),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.account_tree_outlined,
|
||||
'关系',
|
||||
context.l10n.settingsUserMemoryFieldRelationship,
|
||||
_text(member.relationship),
|
||||
),
|
||||
_buildInfoRow(
|
||||
Icons.phone_outlined,
|
||||
'联系方式',
|
||||
context.l10n.settingsUserMemoryFieldContact,
|
||||
_text(member.preferredContactChannel),
|
||||
),
|
||||
_buildInfoRow(Icons.note_outlined, '备注', _text(member.notes)),
|
||||
_buildInfoRow(
|
||||
Icons.note_outlined,
|
||||
context.l10n.settingsUserMemoryFieldNotes,
|
||||
_text(member.notes),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -435,7 +462,7 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
|
||||
Widget _buildTags(List<String> tags) {
|
||||
if (tags.isEmpty) {
|
||||
return _buildEmptyTip('暂无数据');
|
||||
return _buildEmptyTip(context.l10n.memoryNoInfo);
|
||||
}
|
||||
return Wrap(
|
||||
spacing: AppSpacing.sm,
|
||||
@@ -502,21 +529,21 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
|
||||
|
||||
String _text(String? value) {
|
||||
final raw = value?.trim() ?? '';
|
||||
return raw.isEmpty ? '未设置' : raw;
|
||||
return raw.isEmpty ? context.l10n.settingsUnset : raw;
|
||||
}
|
||||
|
||||
String _listText(List<String> values) {
|
||||
if (values.isEmpty) return '未设置';
|
||||
if (values.isEmpty) return context.l10n.settingsUnset;
|
||||
return values.join('、');
|
||||
}
|
||||
|
||||
String _intListText(List<int> values, {required String suffix}) {
|
||||
if (values.isEmpty) return '未设置';
|
||||
if (values.isEmpty) return context.l10n.settingsUnset;
|
||||
return values.map((value) => '$value$suffix').join('、');
|
||||
}
|
||||
|
||||
String _timeWindowSummary(List<TimeWindow> windows) {
|
||||
if (windows.isEmpty) return '未设置';
|
||||
return '${windows.length} 个时段';
|
||||
if (windows.isEmpty) return context.l10n.settingsUnset;
|
||||
return context.l10n.settingsWorkMemoryTimeWindowCount(windows.length);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user