import SwiftUI
struct DocumentTemplatesView: View {
@StateObject private var documentService = DocumentService.shared
@State private var selectedCategory: DocumentCategory?
@State private var selectedTemplate: DocumentTemplate?
@State private var searchText = ""
var body: some View {
NavigationView {
VStack(spacing: 0) {
// Search Bar
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.secondary)
TextField("搜索模板", text: $searchText)
if !searchText.isEmpty {
Button(action: { searchText = "" }) {
Image(systemName: "xmark.circle.fill")
.foregroundColor(.secondary)
}
}
}
.padding()
.background(Color(.systemBackground))
// Templates List
if documentService.isLoading && documentService.categories.isEmpty {
ProgressView("加载中...")
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else if documentService.categories.isEmpty {
VStack(spacing: 20) {
Image(systemName: "doc.text")
.font(.system(size: 60))
.foregroundColor(.gray)
Text("暂无文书模板")
.font(.headline)
.foregroundColor(.gray)
Button("重新加载") {
Task { @MainActor in
try? await documentService.fetchCategories()
}
}
.buttonStyle(.bordered)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else {
ScrollView {
LazyVStack(spacing: 16) {
ForEach(filteredCategories) { category in
CategorySection(category: category, searchText: searchText) { template in
selectedTemplate = template
}
}
}
.padding()
}
}
}
.navigationTitle("文书模板")
.sheet(item: $selectedTemplate) { template in
if let category = documentService.categories.first(where: { $0.templates.contains(where: { $0.id == template.id }) }) {
DocumentFormView(template: template, categoryType: category.id)
}
}
.task {
if documentService.categories.isEmpty {
_ = try? await documentService.fetchCategories()
}
}
}
}
private var filteredCategories: [DocumentCategory] {
if searchText.isEmpty {
return documentService.categories
}
return documentService.categories.compactMap { category in
let filteredTemplates = category.templates.filter { template in
template.name.localizedCaseInsensitiveContains(searchText) ||
template.desc.localizedCaseInsensitiveContains(searchText)
}
if filteredTemplates.isEmpty {
return nil
}
return DocumentCategory(
id: category.id,
name: category.name,
icon: category.icon,
description: category.description,
templates: filteredTemplates
)
}
}
}
// MARK: - Category Section
struct CategorySection: View {
let category: DocumentCategory
let searchText: String
let onTemplateSelect: (DocumentTemplate) -> Void
var body: some View {
VStack(alignment: .leading, spacing: 12) {
// Category Header
HStack {
Image(systemName: iconName(for: category.icon))
.font(.title2)
.foregroundColor(.blue)
VStack(alignment: .leading, spacing: 4) {
Text(category.name)
.font(.headline)
Text(category.description)
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
Text("\(category.templates.count)个")
.font(.caption)
.foregroundColor(.secondary)
}
.padding()
.background(Color(.systemBackground))
.cornerRadius(12)
// Templates
ForEach(category.templates) { template in
Button(action: { onTemplateSelect(template) }) {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text(template.name)
.font(.subheadline)
.foregroundColor(.primary)
Text(template.desc)
.font(.caption)
.foregroundColor(.secondary)
.lineLimit(2)
}
Spacer()
Image(systemName: "chevron.right")
.font(.caption)
.foregroundColor(.gray)
}
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
}
}
}
}
private func iconName(for icon: String) -> String {
switch icon {
case "gavel": return "gavel.fill"
case "file-signature": return "doc.text.fill"
case "building": return "building.2.fill"
case "shield-check": return "checkmark.shield.fill"
case "balance-scale": return "scale.3d"
case "heart": return "heart.fill"
case "globe": return "globe"
case "lightbulb": return "lightbulb.fill"
case "briefcase": return "briefcase.fill"
case "house": return "house.fill"
default: return "doc.text"
}
}
}
#Preview {
DocumentTemplatesView()
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
Tags: