feat(apps): 重构 UI 架构为 presentation 层并新增 l10n 国际化支持

This commit is contained in:
qzl
2026-03-27 14:05:03 +08:00
parent b1f0eb8921
commit c592cc7854
178 changed files with 10748 additions and 5764 deletions
@@ -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;
@@ -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)),
),
],
@@ -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) {
@@ -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);
}
}
@@ -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,
],
),
],
],
@@ -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(),
),
@@ -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),
),
],
),
@@ -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';
}
}
@@ -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),
),
],
),
@@ -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);
}
}