392 lines
14 KiB
Swift
392 lines
14 KiB
Swift
//
|
|
// InviteListView.swift
|
|
// SFI
|
|
//
|
|
// Created by Mac on 2024/10/21.
|
|
//
|
|
|
|
import Foundation
|
|
import SwiftUI
|
|
import ApplicationLibrary
|
|
|
|
//InviteListView
|
|
struct InviteListView : View{
|
|
|
|
@Binding var isPresented: Bool
|
|
|
|
@State private var codeslist: [InviteCode] = []
|
|
@State private var isLoading = false
|
|
@State private var errorMessage: String?
|
|
@State private var alert: Alert?
|
|
@State private var stat: [Int]?
|
|
|
|
@Environment(\.openURL) private var openURL
|
|
|
|
@Environment(\.presentationMode) var presentationMode // 环境变量用于控制视图的呈现
|
|
|
|
var body: some View {
|
|
NavigationView(content: {
|
|
VStack {
|
|
|
|
// Navigation Bar
|
|
ZStack{
|
|
|
|
|
|
HStack{
|
|
|
|
Button(action: {
|
|
isPresented = false
|
|
presentationMode.wrappedValue.dismiss() // 手动触发返回
|
|
}, label: {
|
|
|
|
Image(systemName: "chevron.left")
|
|
.foregroundColor(.white)
|
|
|
|
Text("返回")
|
|
.foregroundColor(.white)
|
|
}).padding(10) // 增加可点击区域
|
|
Spacer()
|
|
|
|
Button(action: {
|
|
Task{
|
|
await genoneInvite()
|
|
}
|
|
}, label: {
|
|
Text("生成邀请码").foregroundColor(.white)
|
|
})
|
|
|
|
}
|
|
Spacer()
|
|
VStack(spacing: 5){
|
|
|
|
Text("我的邀请")
|
|
.fontWeight(.bold).lineLimit(1).frame(width: UIScreen.main.bounds.width*0.5)
|
|
|
|
|
|
}
|
|
.foregroundColor(.white)
|
|
Spacer()
|
|
|
|
|
|
}
|
|
.padding(.all)
|
|
|
|
|
|
|
|
if isLoading {
|
|
ProgressView()
|
|
.progressViewStyle(CircularProgressViewStyle())
|
|
.padding()
|
|
}
|
|
|
|
if let errorMessage = errorMessage {
|
|
Text(errorMessage)
|
|
.bold()
|
|
.foregroundColor(.red)
|
|
.multilineTextAlignment(.center) // 确保多行文本居中
|
|
.padding()
|
|
}
|
|
|
|
//
|
|
// // Subscription Plans
|
|
|
|
ScrollView(.vertical, showsIndicators: false) {
|
|
VStack(spacing: 16) {
|
|
VStack {
|
|
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 2), alignment: .leading) {
|
|
if let st = stat {
|
|
StatusItem("邀请注册人数") {
|
|
|
|
StatusLine("人数", "\(st[0])")
|
|
}
|
|
StatusItem("邀请佣金比例") {
|
|
|
|
StatusLine("比例", "\(st[3])%")
|
|
}
|
|
StatusItem("确认中的佣金") {
|
|
|
|
StatusLine("金额(¥)", "\(st[1])")
|
|
}
|
|
StatusItem("累计获得佣金") {
|
|
|
|
StatusLine("金额(¥)", "\(st[2])")
|
|
}
|
|
}
|
|
|
|
|
|
}.background {
|
|
GeometryReader { geometry in
|
|
Rectangle()
|
|
.fill(.clear)
|
|
.frame(height: 1)
|
|
.onChangeCompat(of: geometry.size.width) { newValue in
|
|
//updateColumnCount(newValue)
|
|
}
|
|
.onAppear {
|
|
// updateColumnCount(geometry.size.width)
|
|
}
|
|
}.padding()
|
|
}
|
|
}
|
|
.frame(alignment: .topLeading)
|
|
.padding([.top, .leading, .trailing])
|
|
|
|
if codeslist.isEmpty {
|
|
// Text("Loading plans...") // Loading indicator
|
|
if !isLoading
|
|
{
|
|
Text("暂无记录").font(.caption)
|
|
.foregroundColor(.gray)
|
|
.multilineTextAlignment(.center) // 确保多行文本居中
|
|
.padding()
|
|
}
|
|
} else {
|
|
|
|
ForEach(codeslist) { plan in
|
|
Button(action: {
|
|
UIPasteboard.general.string = UserManager.shared.mainregisterURL()+plan.code
|
|
alert = Alert(okMessage: "复制成功: " + UserManager.shared.mainregisterURL()+plan.code )
|
|
}, label: {
|
|
InviteItemView(invicode: plan)
|
|
})
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}.padding(.top, 20)
|
|
|
|
Spacer()
|
|
|
|
// Subscription Notice
|
|
Text(" ")
|
|
.font(.footnote)
|
|
.foregroundColor(.gray)
|
|
.padding(.bottom, 20)
|
|
}
|
|
.background(
|
|
LinearGradient(colors: [
|
|
|
|
Color("BG1"),
|
|
Color("BG1"),
|
|
Color("BG2"),
|
|
Color("BG2"),
|
|
|
|
], startPoint: .top, endPoint: .bottom)
|
|
)
|
|
.navigationBarHidden(true)
|
|
// .background(.white)
|
|
.onAppear(){
|
|
Task{
|
|
await getCodesList()
|
|
}
|
|
}.edgesIgnoringSafeArea(.bottom)
|
|
|
|
})
|
|
.navigationBarHidden(true)
|
|
.navigationBarBackButtonHidden(true) // 隐藏返回按钮
|
|
.alertBinding($alert)
|
|
|
|
}
|
|
|
|
public func genoneInvite() async {
|
|
|
|
|
|
let userInfoUrl = URL(string: "\(UserManager.shared.baseURL())user/invite/save")!
|
|
var request = URLRequest(url: userInfoUrl)
|
|
request.httpMethod = "GET"
|
|
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
request.addValue(UserManager.shared.getAutoData(), forHTTPHeaderField: "Authorization")
|
|
|
|
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
|
DispatchQueue.main.async {
|
|
if let error = error {
|
|
alert = Alert(error)
|
|
return
|
|
}
|
|
|
|
guard let data = data else {
|
|
return
|
|
}
|
|
|
|
|
|
struct InviteReponse2: Codable {
|
|
let status, message: String?
|
|
}
|
|
// Parse the user info response
|
|
if let response = try? JSONDecoder().decode(InviteReponse2.self, from: data),let message = response.message {
|
|
|
|
if let status = response.status, status == "success" {
|
|
alert = Alert(okMessage: message){
|
|
|
|
Task{
|
|
await getCodesList()
|
|
}
|
|
}
|
|
}else{
|
|
alert = Alert(errorMessage: message)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
task.resume()
|
|
}
|
|
|
|
|
|
private struct StatusLine: View {
|
|
private let name: String
|
|
private let value: String
|
|
|
|
init(_ name: String, _ value: String) {
|
|
self.name = name
|
|
self.value = value
|
|
}
|
|
|
|
var body: some View {
|
|
HStack {
|
|
Text(name)
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
Spacer()
|
|
Text(value)
|
|
.font(.subheadline)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct StatusItem<T>: View where T: View {
|
|
private let title: String
|
|
@ViewBuilder private let content: () -> T
|
|
|
|
init(_ title: String, @ViewBuilder content: @escaping () -> T) {
|
|
self.title = title
|
|
self.content = content
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading) {
|
|
HStack {
|
|
Text(title)
|
|
.font(.subheadline)
|
|
Spacer()
|
|
}.padding(.bottom, 8)
|
|
content()
|
|
}
|
|
.frame(minWidth: 125, alignment: .topLeading)
|
|
#if os(tvOS)
|
|
.padding(EdgeInsets(top: 20, leading: 26, bottom: 20, trailing: 26))
|
|
#else
|
|
.padding(EdgeInsets(top: 10, leading: 13, bottom: 10, trailing: 13))
|
|
#endif
|
|
.background(backgroundColor.opacity(0.3))
|
|
.cornerRadius(10)
|
|
}
|
|
|
|
private var backgroundColor: Color {
|
|
#if os(iOS)
|
|
return Color(.gray)
|
|
#elseif os(macOS)
|
|
return Color(nsColor: .textBackgroundColor)
|
|
#elseif os(tvOS)
|
|
return Color(uiColor: .black)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
public func getCodesList() async {
|
|
|
|
isLoading = true
|
|
errorMessage = nil
|
|
|
|
|
|
let userInfoUrl = URL(string: "\(UserManager.shared.baseURL())user/invite/fetch")!
|
|
var request = URLRequest(url: userInfoUrl)
|
|
request.httpMethod = "GET"
|
|
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
request.addValue(UserManager.shared.getAutoData(), forHTTPHeaderField: "Authorization")
|
|
|
|
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
|
DispatchQueue.main.async {
|
|
self.isLoading = false
|
|
|
|
if let error = error {
|
|
self.errorMessage = "数据请求失败: \(error.localizedDescription)"
|
|
return
|
|
}
|
|
|
|
guard let data = data else {
|
|
self.errorMessage = "数据请求失败"
|
|
return
|
|
}
|
|
|
|
|
|
|
|
// if let jsonString = String(data: data, encoding: .utf8) {
|
|
// print("Response data: \(jsonString)")
|
|
// } else {
|
|
// print("Failed to convert data to string.")
|
|
// }
|
|
//
|
|
|
|
// Parse the user info response
|
|
if let Subscribe = try? JSONDecoder().decode(InviteReponse.self, from: data) {
|
|
|
|
if let codes = Subscribe.data?.codes {
|
|
codeslist = codes
|
|
stat = Subscribe.data?.stat
|
|
}else{
|
|
self.errorMessage = Subscribe.message ?? ""
|
|
}
|
|
|
|
}else{
|
|
self.errorMessage = "数据请求失败: JSON 解析错误"
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
task.resume()
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct InviteItemView: View {
|
|
var invicode : InviteCode
|
|
|
|
var body: some View {
|
|
HStack {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
|
|
Text("邀请码:" + invicode.code )
|
|
.font(.headline)
|
|
.foregroundColor(.black).multilineTextAlignment(.leading)
|
|
.frame(maxWidth: .infinity)
|
|
|
|
Spacer()
|
|
|
|
|
|
Text("创建时间:"+TimestampConverter.convertTimestampToDateString(invicode.createdAt))
|
|
.font(.caption)
|
|
.fontWeight(.bold)
|
|
.padding(2)
|
|
.frame(maxWidth: .infinity)
|
|
.foregroundColor(.gray)
|
|
// .offset(x: -16, y:16)
|
|
.offset(y:10)
|
|
|
|
|
|
}
|
|
|
|
}
|
|
.padding()
|
|
.background(Color.white)
|
|
.cornerRadius(10)
|
|
.shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 3)
|
|
.padding(.horizontal, 20)
|
|
}
|
|
}
|