// // SupportticketView.swift // SFI // // Created by Mac on 2024/10/20. // import Foundation import SwiftUI import ApplicationLibrary struct SupportTicketView : View { var edges = getSafeAreaInsets() @State var selectedTab = "Chats" @Namespace var animation @Environment(\.presentationMode) var presentationMode // 环境变量用于控制视图的呈现 @State private var TicketsList: [TicketsReponseDatum] = [] @State private var isLoading = false @State private var errorMessage: String? @State private var alert: Alert? @State private var showInputSheet = false @State private var tickettitle = "" @State private var userInput = "" @State private var isInputLoading = false @State private var submitmessage = "" var body: some View{ NavigationView(content: { VStack(spacing: 0){ VStack{ ZStack{ HStack{ Button(action: { presentationMode.wrappedValue.dismiss() // 手动触发返回 }, label: { Image(systemName: "chevron.left") .foregroundColor(.white) Text("返回") .foregroundColor(.white) }).padding(10) // 增加可点击区域 Spacer(minLength: 0) Button(action: { showInputSheet.toggle() }, label: { // Image(systemName: "note.text.badge.plus") Text("提交新工单").foregroundColor(.red) }) } Text("我的工单").fontWeight(.bold).lineLimit(1) } .padding() // .padding(.top,edges.top) .foregroundColor(.white) // HStack(spacing: 20){ // // ForEach(tabs,id: \.self){title in // // TabButton(selectedTab: $selectedTab, title: title, animation: animation) // } // } // .padding() // .background(Color.white.opacity(0.08)) // .cornerRadius(15) // .padding(.vertical) } .padding(.bottom) // .background(Color("top")) // .clipShape(CustomCorner(corner: .bottomLeft, size: 65)) ZStack{ // Color("top") // Color("bg") // .clipShape(CustomCorner(corner: .topRight, size: 65)) ScrollView(.vertical, showsIndicators: false, content: { VStack(spacing: 20){ /*HStack{ Text("All Chats") .font(.title2) .fontWeight(.bold) Spacer(minLength: 0) Button(action: {}, label: { Image(systemName: "slider.horizontal.3") .font(.system(size: 22)) .foregroundColor(.primary) }) } .padding([.horizontal,.top])*/ if isLoading { ProgressView() .progressViewStyle(CircularProgressViewStyle()) .padding() } if let errorMessage = errorMessage { Text(errorMessage) .bold() .foregroundColor(.red) .multilineTextAlignment(.center) // 确保多行文本居中 .padding() } if TicketsList.count > 0 { ForEach(TicketsList){chatData in // Chat View... NavigationLink(destination: SupportTicketChatView(ticketID: chatData.idOLD)) { ChatView(chatData: chatData) } } }else{ if !isLoading { Text("暂无记录").font(.caption) .foregroundColor(.gray) .multilineTextAlignment(.center) // 确保多行文本居中 .padding() } } } .padding(.vertical) }) //.clipShape(CustomCorner(corner: .topRight, size: 65)) // its cutting off inside view may be its a bug.... } }.onAppear(){ Task{ await getPlanList() } } .navigationBarHidden(true) //.background(Color("bg").ignoresSafeArea(.all, edges: .all)) .background( LinearGradient(colors: [ Color("BG1"), Color("BG1"), Color("BG2"), Color("BG2"), ], startPoint: .top, endPoint: .bottom).ignoresSafeArea(.all, edges: .all)) // .ignoresSafeArea(.all, edges: .top) .sheet(isPresented: $showInputSheet) { InputSheet(tickettitle: $tickettitle, userInput: $userInput, isInputLoading: $isInputLoading, submitmessage: $submitmessage, onSubmit: { Task { // 提交网络请求 await submitTicketData(title: tickettitle, userInput: userInput) } }) } }) .navigationBarHidden(true) .alertBinding($alert) .navigationBarBackButtonHidden(true) // 隐藏返回按钮 } public func submitTicketData(title: String, userInput: String) async{ isInputLoading = true let userInfoUrl = URL(string: "\(UserManager.shared.baseURL())user/ticket/save?subject=\(title)&level=0&message=\(userInput)")! var request = URLRequest(url: userInfoUrl) request.httpMethod = "POST" 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 { isInputLoading = false if let error = error { submitmessage = ( "数据请求失败: \(error.localizedDescription)") return } guard let data = data else { submitmessage = ( "数据请求失败") return } // MARK: - TicketSendMsgReponse struct TicketSendMsgReponse: Codable { let status, message: String? } // Parse the user info response if let SendMsgReponse = try? JSONDecoder().decode(TicketSendMsgReponse.self, from: data), let message = SendMsgReponse.message { if let stutas = SendMsgReponse.status , stutas == "success" { submitmessage = message //1s 后 关闭窗口 withAnimation { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { showInputSheet = false //刷新界面 Task{ await getPlanList() } } } }else{ submitmessage = (message) } }else{ submitmessage = ( "数据请求失败: JSON 解析错误") } } } task.resume() } public func getPlanList() async { isLoading = true errorMessage = nil let userInfoUrl = URL(string: "\(UserManager.shared.baseURL())user/ticket/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 } // Parse the user info response if let Subscribe = try? JSONDecoder().decode(TicketsReponse.self, from: data) { if let data = Subscribe.data { TicketsList = data }else{ self.errorMessage = Subscribe.message ?? "" } }else{ self.errorMessage = "数据请求失败: JSON 解析错误" } } } task.resume() } } var tabs = ["Chats","Status","Calls"] struct TabButton : View { @Binding var selectedTab : String var title : String var animation : Namespace.ID var body: some View{ Button(action: { withAnimation{ selectedTab = title } }, label: { Text(title) .foregroundColor(.white) .padding(.vertical,10) .padding(.horizontal) // Sliding Effect... .background( ZStack{ if selectedTab == title{ RoundedRectangle(cornerRadius: 10) .fill(Color("top")) .matchedGeometryEffect(id: "Tab", in: animation) } } ) }) } } struct CustomCorner : Shape { var corner : UIRectCorner var size : CGFloat func path(in rect: CGRect) -> Path { let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corner, cornerRadii: CGSize(width: size, height: size)) return Path(path.cgPath) } } struct ChatView : View { var chatData : TicketsReponseDatum var body: some View{ HStack(spacing: 10){ VStack(spacing: 12) { HStack { VStack(alignment: .leading, spacing: 8, content: { Text(chatData.subject) .fontWeight(.bold) .lineLimit(1).foregroundColor(.white) // Text("官方回复:\(chatData.message ?? "未回复")") // .font(.caption) // .lineLimit(1) HStack(content: { if(chatData.status == 1){ Text("工单状态 : 已关闭").fontWeight(.light).foregroundColor(.gray).font(.subheadline) }else if(chatData.status == 0){ Text("工单状态 : 待回复").fontWeight(.light).foregroundColor(.red).font(.subheadline) }else if(chatData.status == 2){ Text("工单状态 : 已回复").fontWeight(.light).foregroundColor(.green).font(.subheadline) } Spacer(minLength: 0) Text("\(TimestampConverter.convertTimestampToDateString(chatData.updatedAt))") .font(.subheadline).foregroundColor(.white) }) }) } Divider() } } .padding(.horizontal) } } // Model And Sample Data.... // //struct Chat : Identifiable { // // var id = UUID().uuidString // var name : String // var image : String // var msg : String // var time : String //} // //// were going to do custom grouping of views.... // //struct HomeData { // // var groupName : String // var groupData : [Chat] //} //var FriendsChat : [Chat] = [ // // Chat(name: "iJustine",image: "p0", msg: "Hey EveryOne !!!", time: "02:45"), // Chat(name: "Kavsoft",image: "p1", msg: "Learn - Develop - Deploy", time: "03:45"), // Chat(name: "SwiftUI",image: "p2", msg: "New Framework For iOS", time: "04:55"), // Chat(name: "Bill Gates",image: "p3", msg: "Founder Of Microsoft", time: "06:25"), // Chat(name: "Tim Cook",image: "p4", msg: "Apple lnc CEO", time: "07:19"), // Chat(name: "Jeff",image: "p5", msg: "I dont Know How To Spend Money :)))", time: "08:22"), //] // //var GroupChat : [Chat] = [ // // Chat(name: "iTeam",image: "p0", msg: "Hey EveryOne !!!", time: "02:45"), // Chat(name: "Kavsoft - Developers",image: "p1", msg: "Next Video :))))", time: "03:45"), // Chat(name: "SwiftUI - Community",image: "p2", msg: "New File Importer/Exporter", time: "04:55"), //] // //var data = [ // // // Group 1 // HomeData(groupName: "", groupData: FriendsChat), //] struct InputSheet: View { @Binding var tickettitle: String @Binding var userInput: String @Binding var isInputLoading: Bool @Binding var submitmessage:String var onSubmit: () -> Void var body: some View { VStack(spacing: 20) { Spacer() Text("新的工单").fontWeight(.bold).lineLimit(1) Text("请输入工单标题").font(.subheadline) .foregroundColor(.gray) .frame(maxWidth: .infinity) .multilineTextAlignment(.leading) .padding(.leading, 5) // 添加左侧内边距 .padding(.top, 8) // 添加顶部内边距 TextEditor( text: $tickettitle) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() .frame(width: UIScreen.main.bounds.width * 0.9,height: 50) .background(Color.clear) // 设置背景为透明 .cornerRadius(10) // 圆角 .overlay( RoundedRectangle(cornerRadius: 10) .stroke(Color.gray.opacity(0.5), lineWidth: 1) ) // 添加边框 .background(Color.clear) // 设置背景为透明// 添加边框 .font(.system(size: 18)) .transparentScrolling() Text("请输入工单内容").font(.subheadline) .foregroundColor(.gray) .frame(maxWidth: .infinity) .padding(.leading, 5) // 添加左侧内边距 .padding(.top, 8) // 添加顶部内边距 .multilineTextAlignment(.leading) TextEditor(text: $userInput) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() .frame(width: UIScreen.main.bounds.width * 0.9,height: UIScreen.main.bounds.height * 0.4) .background(Color.clear) // 设置背景为透明 .cornerRadius(10) // 圆角 .overlay( RoundedRectangle(cornerRadius: 10) .stroke(Color.gray.opacity(0.5), lineWidth: 1) ) // 添加边框 .font(.system(size: 18)) .transparentScrolling() Spacer() if !submitmessage.isEmpty{ Text(submitmessage).font(.subheadline).foregroundColor(Color.red.opacity(0.9)) } Button(action: { if( tickettitle.count > 0 && userInput.count > 0){ onSubmit() // 提交时触发回调 } }) { if isInputLoading { ProgressView() .progressViewStyle(CircularProgressViewStyle()).tint(Color("Main")) } else { Text("提交工单") .frame(maxWidth: .infinity) .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(10) } } .padding() .frame(maxWidth: .infinity) .padding(.bottom, 20) } .background(Color("bg").ignoresSafeArea(.all, edges: .all)) .ignoresSafeArea(edges: .bottom) // 忽略安全区域,保证按钮位置靠底部 } }