From 3a745476840f014e1151c0fe260cdd5ed9b4d897 Mon Sep 17 00:00:00 2001 From: edwinQQQ Date: Wed, 23 Jul 2025 20:15:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=8A=9F=E8=83=BD=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E8=A7=86=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在MainFeature中集成AppSettingFeature,支持应用设置页面的导航与状态管理。 - 新增AppSettingView以展示用户头像和昵称编辑功能,提升用户体验。 - 更新MainView以支持应用设置页面的展示,增强导航功能。 --- yana/Features/AppSettingFeature.swift | 27 ++++++++ yana/Features/MainFeature.swift | 20 +++++- yana/Views/AppSettingView.swift | 98 +++++++++++++++++++++++++++ yana/Views/MainView.swift | 12 ++++ 4 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 yana/Features/AppSettingFeature.swift create mode 100644 yana/Views/AppSettingView.swift diff --git a/yana/Features/AppSettingFeature.swift b/yana/Features/AppSettingFeature.swift new file mode 100644 index 0000000..224694e --- /dev/null +++ b/yana/Features/AppSettingFeature.swift @@ -0,0 +1,27 @@ +import Foundation +import ComposableArchitecture + +struct AppSettingFeature: Reducer { + struct State: Equatable { + var nickname: String = "hahahaha" + var avatarURL: String? = nil + } + enum Action: Equatable { + case onAppear + case editNicknameTapped + case logoutTapped + // 可扩展更多 action + } + func reduce(into state: inout State, action: Action) -> Effect { + switch action { + case .onAppear: + return .none + case .editNicknameTapped: + // 预留编辑昵称逻辑 + return .none + case .logoutTapped: + // 预留登出逻辑 + return .none + } + } +} \ No newline at end of file diff --git a/yana/Features/MainFeature.swift b/yana/Features/MainFeature.swift index 67d2ad5..ea7b3c1 100644 --- a/yana/Features/MainFeature.swift +++ b/yana/Features/MainFeature.swift @@ -15,12 +15,14 @@ struct MainFeature: Reducer { // 新增:导航路径和设置页面 State var navigationPath: [Destination] = [] var settingState: SettingFeature.State? = nil + var appSettingState: AppSettingFeature.State? = nil } // 新增:导航目标 enum Destination: Hashable, Equatable { case setting case test + case appSetting } @CasePathable @@ -35,6 +37,8 @@ struct MainFeature: Reducer { case settingButtonTapped case settingAction(SettingFeature.Action) case testButtonTapped + case appSettingButtonTapped + case appSettingAction(AppSettingFeature.Action) } var body: some ReducerOf { @@ -66,8 +70,8 @@ struct MainFeature: Reducer { return .none case .me(.settingButtonTapped): // 触发 push 到设置页 - state.settingState = SettingFeature.State() - state.navigationPath.append(.setting) + state.appSettingState = AppSettingFeature.State() + state.navigationPath.append(.appSetting) return .none case .me: return .none @@ -76,6 +80,9 @@ struct MainFeature: Reducer { if !newPath.contains(.setting) { state.settingState = nil } + if !newPath.contains(.appSetting) { + state.appSettingState = nil + } state.navigationPath = newPath return .none case .settingButtonTapped: @@ -87,11 +94,20 @@ struct MainFeature: Reducer { case .testButtonTapped: state.navigationPath.append(.test) return .none + case .appSettingButtonTapped: + state.appSettingState = AppSettingFeature.State() + state.navigationPath.append(.appSetting) + return .none + case .appSettingAction: + return .none } } // 设置页作用域 .ifLet(\ .settingState, action: /Action.settingAction) { SettingFeature() } + .ifLet(\ .appSettingState, action: /Action.appSettingAction) { + AppSettingFeature() + } } } diff --git a/yana/Views/AppSettingView.swift b/yana/Views/AppSettingView.swift new file mode 100644 index 0000000..5227bc8 --- /dev/null +++ b/yana/Views/AppSettingView.swift @@ -0,0 +1,98 @@ +import SwiftUI +import ComposableArchitecture + +struct AppSettingView: View { + let store: StoreOf + + var body: some View { + WithViewStore(self.store, observe: { $0 }) { viewStore in + ZStack { + Color(red: 22/255, green: 17/255, blue: 44/255).ignoresSafeArea() + VStack(spacing: 0) { + Spacer().frame(height: 24) + // 顶部标题 + Text("Edit") + .font(.system(size: 22, weight: .semibold)) + .foregroundColor(.white) + .padding(.top, 8) + // 头像 + ZStack(alignment: .bottomTrailing) { + Image("avatar_placeholder") + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 120, height: 120) + .clipShape(Circle()) + Button(action: {}) { + ZStack { + Circle().fill(Color.purple).frame(width: 36, height: 36) + Image(systemName: "camera.fill") + .foregroundColor(.white) + } + } + .offset(x: 8, y: 8) + } + .padding(.top, 24) + // 昵称 + HStack { + Text("Nickname") + .foregroundColor(.white) + Spacer() + Text(viewStore.nickname) + .foregroundColor(.gray) + Image(systemName: "chevron.right") + .foregroundColor(.gray) + } + .padding(.horizontal, 32) + .padding(.vertical, 18) + .onTapGesture { + viewStore.send(.editNicknameTapped) + } + Divider().background(Color.gray.opacity(0.3)) + .padding(.horizontal, 32) + // 其他设置项 + VStack(spacing: 0) { + settingRow("Personal Information and Permissions") + settingRow("Help") + settingRow("Clear Cache") + settingRow("Check for Updates") + settingRow("Log Out") + settingRow("About Us") + } + .background(Color.clear) + .padding(.horizontal, 0) + Spacer() + // 底部大按钮 + Button(action: { + viewStore.send(.logoutTapped) + }) { + Text("Log out of account") + .font(.system(size: 18, weight: .semibold)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding(.vertical, 18) + .background(Color.white.opacity(0.08)) + .cornerRadius(28) + .padding(.horizontal, 32) + } + .padding(.bottom, 32) + } + } + } + } + // 设置项行 + func settingRow(_ title: String) -> some View { + return VStack(spacing: 0) { + HStack { + Text(title) + .foregroundColor(.white) + Spacer() + Image(systemName: "chevron.right") + .foregroundColor(.gray) + } + .padding(.horizontal, 32) + .padding(.vertical, 18) + Divider().background(Color.gray.opacity(0.3)) + .padding(.horizontal, 32) + } + } +} \ No newline at end of file diff --git a/yana/Views/MainView.swift b/yana/Views/MainView.swift index c144a64..8bfd671 100644 --- a/yana/Views/MainView.swift +++ b/yana/Views/MainView.swift @@ -78,6 +78,18 @@ struct MainView: View { ) case .test: TestPushView() + case .appSetting: + IfLetStore( + self.store.scope( + state: \.appSettingState, + action: MainFeature.Action.appSettingAction + ), + then: { appSettingStore in + WithPerceptionTracking { + AppSettingView(store: appSettingStore) + } + } + ) } } }