This commit is contained in:
anlicheng 2026-03-20 00:33:02 +08:00
parent 81ae103730
commit 41cacb7134
3 changed files with 154 additions and 39 deletions

View File

@ -9,6 +9,8 @@ import SwiftUI
struct SettingsAboutView: View { struct SettingsAboutView: View {
@Environment(\.openURL) private var openURL @Environment(\.openURL) private var openURL
@State private var isShowingFeedbackSheet = false
var body: some View { var body: some View {
ScrollView(.vertical, showsIndicators: false) { ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading, spacing: 32) { VStack(alignment: .leading, spacing: 32) {
@ -64,9 +66,7 @@ struct SettingsAboutView: View {
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
.onTapGesture { .onTapGesture {
if let url = URL(string: "https://yourfeedbacklink.com") { self.isShowingFeedbackSheet = true
openURL(url)
}
} }
} }
.background(Color.primary.opacity(0.03)) .background(Color.primary.opacity(0.03))
@ -110,6 +110,23 @@ struct SettingsAboutView: View {
.padding(32) .padding(32)
.frame(maxWidth: 600, alignment: .leading) .frame(maxWidth: 600, alignment: .leading)
} }
.sheet(isPresented: $isShowingFeedbackSheet) {
VStack {
HStack {
Spacer()
Button {
isShowingFeedbackSheet = false
} label: {
Text("关闭")
}
.buttonStyle(.plain)
.padding()
}
//
SettingsUserIssueView()
}
.frame(width: 500, height: 600) //
}
} }
} }

View File

@ -4,50 +4,148 @@
// //
// Created by on 2026/1/19. // Created by on 2026/1/19.
// //
import SwiftUI import SwiftUI
// MARK: - ()
struct SettingsUserIssueView: View { struct SettingsUserIssueView: View {
//
@State private var account: String = "" @State private var account: String = ""
@State private var text: String = "" @State private var text: String = ""
var body: some View { //
VStack { @State private var isSubmitting: Bool = false
Text("用户反馈") @State private var showSuccessToast: Bool = false
TextField("", text: $account) var body: some View {
.multilineTextAlignment(.leading) ZStack {
.textFieldStyle(PlainTextFieldStyle()) //
.frame(width: 200, height: 25) ScrollView(.vertical, showsIndicators: false) {
.background(Color.clear) VStack(alignment: .leading, spacing: 24) {
.foregroundColor(Color.black)
.overlay( // 1.
Rectangle() HStack(spacing: 12) {
.frame(height: 1) Image(systemName: "envelope.badge.fill")
.foregroundColor(.blue) .foregroundColor(.blue)
.padding(.top, 25) Text("用户反馈")
, alignment: .topLeading) .font(.system(size: 18, weight: .bold))
}
.padding(.leading, 4)
// 2.
VStack(alignment: .leading, spacing: 20) {
//
VStack(alignment: .leading, spacing: 8) {
Text("联系方式 (选填)")
.font(.caption.bold())
.foregroundColor(.secondary)
TextField("邮箱或用户名", text: $account)
.textFieldStyle(.plain)
.padding(10)
.background(Color.primary.opacity(0.04))
.cornerRadius(8)
}
// ( Placeholder )
VStack(alignment: .leading, spacing: 8) {
Text("问题描述").font(.caption.bold()).foregroundColor(.secondary)
ZStack(alignment: .topLeading) {
if text.isEmpty {
Text("请详细描述您遇到的问题...")
.foregroundColor(.gray.opacity(0.5))
.padding(.horizontal, 12).padding(.vertical, 12)
.font(.system(size: 14))
}
TextEditor(text: $text) TextEditor(text: $text)
.padding(4) .font(.system(size: 14))
.overlay(alignment: .topLeading) { .scrollContentBackground(.hidden) //
if text.isEmpty { .padding(8)
Text("请输入内容") .background(Color.primary.opacity(0.04))
.foregroundColor(.gray) .cornerRadius(8)
.padding(.leading, 8) }
.frame(minHeight: 160)
} }
} }
.frame(minHeight: 120) .padding(20)
.overlay( .background(Color.primary.opacity(0.02))
RoundedRectangle(cornerRadius: 6) .cornerRadius(12)
.stroke(Color.gray.opacity(0.4)) .overlay(RoundedRectangle(cornerRadius: 12).stroke(Color.primary.opacity(0.05), lineWidth: 1))
)
// 3.
Button(action: submitFeedback) {
HStack {
if isSubmitting {
ProgressView().controlSize(.small).brightness(1)
} else {
Image(systemName: "paperplane.fill")
} }
.padding() Text("发送反馈").fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 12)
.background(text.isEmpty ? Color.gray : Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.buttonStyle(.plain)
.disabled(text.isEmpty || isSubmitting)
}
.padding(32)
.frame(maxWidth: 600, alignment: .leading)
}
.blur(radius: showSuccessToast ? 8 : 0) //
.disabled(showSuccessToast) //
// 4. Overlay
if showSuccessToast {
successPopup
}
}
}
// MARK: -
private func submitFeedback() {
withAnimation { isSubmitting = true }
//
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
isSubmitting = false
showSuccessToast = true
text = "" //
}
// 2.5
DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
withAnimation(.easeOut(duration: 0.3)) {
showSuccessToast = false
}
}
}
}
// MARK: -
private var successPopup: some View {
VStack(spacing: 16) {
Image(systemName: "checkmark.seal.fill")
.font(.system(size: 44))
.foregroundStyle(.green.gradient)
Text("发送成功")
.font(.headline)
Text("感谢您的支持!")
.font(.subheadline)
.foregroundColor(.secondary)
}
.padding(40)
.background(.ultraThinMaterial) // macOS
.cornerRadius(24)
.shadow(color: .black.opacity(0.15), radius: 20)
.transition(.asymmetric(
insertion: .scale(scale: 0.8).combined(with: .opacity),
removal: .opacity.combined(with: .scale(scale: 1.1))
))
} }
} }
#Preview {
SettingsUserIssueView()
}

View File

@ -73,7 +73,7 @@ struct punchnetApp: App {
.environment(self.userContext) .environment(self.userContext)
.environment(self.appContext) .environment(self.appContext)
} }
.defaultSize(width: 800, height: 500) //.defaultSize(width: 800, height: 500)
.defaultPosition(.center) .defaultPosition(.center)
Window("重置密码", id: "resetPassword") { Window("重置密码", id: "resetPassword") {