This commit is contained in:
anlicheng 2025-04-08 17:08:12 +08:00
parent 7468710008
commit cac31802b3
5 changed files with 271 additions and 263 deletions

View File

@ -0,0 +1,53 @@
//
// FollowListModel.swift
// dimensionhub
//
// Created by on 2025/4/8.
//
import Foundation
import Observation
@Observable
final class FollowListModel {
struct DramaItem: Codable {
struct Episode: Codable, Identifiable {
let id = UUID().uuidString
let name: String
let thumb: String
let num_name: String
let play: String
enum CodingKeys: String, CodingKey {
case name, thumb, num_name, play
}
}
let id: Int
let title: String
let episodes: [Episode]
}
struct FavorResponse: Codable {
let dramas: [DramaItem]
}
var dramas: [DramaItem]
init() {
self.dramas = []
}
func loadData(userId: String) async {
let response = await API.getUserFollowList(userId: userId, as: FavorResponse.self)
switch response {
case .error(let code, let message):
print("index load data get error_code: \(code), message: \(message)")
case .result(let result):
await MainActor.run {
self.dramas = result.dramas
}
}
}
}

View File

@ -0,0 +1,144 @@
//
// FavorView.swift
// dimensionhub
//
// Created by on 2025/3/18.
//
import Foundation
import SwiftUI
struct FollowListView: View {
@AppStorage("userId") private var userId: String = Utils.defaultUserId()
@State var followModel = FollowListModel()
@State var isMoreLoading: Bool = false
var body: some View {
VStack(alignment: .center) {
Rectangle()
.frame(height: 0)
.background(Color(hex: "#F2F2F2"), ignoresSafeAreaEdges: .top)
.border(.red)
Group {
if followModel.dramas.count > 0 {
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(alignment: .center) {
ForEach(followModel.dramas, id: \.id) { drama in
DramaCellView(dramaItem: drama)
}
}
}
} else {
VStack {
Spacer()
Text("这里什么都没有")
.font(.system(size: 16))
.foregroundColor(.black)
Spacer()
}
}
}
.frame(width: 370)
}
.ignoresSafeArea(edges: .bottom)
.navigationTitle("番剧补完计划")
.task {
await self.followModel.loadData(userId: self.userId)
}
}
}
extension FollowListView {
//
struct DramaCellView: View {
let dramaItem: FollowListModel.DramaItem
var body: some View {
VStack(alignment: .leading) {
NavigationLink(destination: DetailView(id: dramaItem.id)) {
Text(dramaItem.title)
.font(.system(size: 20))
.foregroundColor(Color(hex: "#333333"))
.lineLimit(1)
}
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .center, spacing: 5) {
ForEach(dramaItem.episodes) { item in
DramaCellEpisodeView(item: item)
}
}
}
}
}
}
struct DramaCellEpisodeView: View {
let item: FollowListModel.DramaItem.Episode
var body: some View {
VStack(alignment: .center) {
GeometryReader { geometry in
AsyncImage(url: URL(string: item.thumb)) { phase in
switch phase {
case .empty:
ProgressView()
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geometry.frame(in: .local).width, height: 80)
.clipped()
default:
Image("ph_img_medium")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geometry.frame(in: .local).width, height: 80)
.clipped()
}
}
.frame(width: geometry.frame(in: .local).width, height: 80)
.overlay(alignment: .topLeading) {
if !item.num_name.isEmpty {
HStack(alignment: .center) {
Text(item.num_name)
.font(.system(size: 12))
.foregroundColor(.white)
.lineLimit(1)
}
.padding(3)
.background(
Color.black.opacity(0.6)
)
.cornerRadius(3)
.padding(3)
} else {
EmptyView()
}
}
}
Text(item.name)
.font(.system(size: 12))
.foregroundColor(Color(hex: "#333333"))
.lineLimit(1)
}
.frame(width: 120, height: 100)
.onTapGesture {
if let playUrl = URL(string: item.play) {
UIApplication.shared.open(playUrl)
}
}
}
}
}
#Preview {
FollowListView()
}

View File

@ -1,179 +0,0 @@
//
// FavorView.swift
// dimensionhub
//
// Created by on 2025/3/18.
//
import Foundation
import SwiftUI
import Observation
@Observable
final class FollowListModel {
struct DramaItem: Codable {
struct Episode: Codable, Identifiable {
let id = UUID().uuidString
let name: String
let thumb: String
let num_name: String
let play: String
enum CodingKeys: String, CodingKey {
case name, thumb, num_name, play
}
}
let id: Int
let title: String
let episodes: [Episode]
}
struct FavorResponse: Codable {
let dramas: [DramaItem]
}
var dramas: [DramaItem]
init() {
self.dramas = []
}
func loadData(userId: String) async {
let response = await API.getUserFollowList(userId: userId, as: FavorResponse.self)
switch response {
case .error(let code, let message):
print("index load data get error_code: \(code), message: \(message)")
case .result(let result):
await MainActor.run {
self.dramas = result.dramas
}
}
}
}
struct FollowListView: View {
@AppStorage("userId") private var userId: String = Utils.defaultUserId()
@State var followModel = FollowListModel()
@State var isMoreLoading: Bool = false
var body: some View {
VStack(alignment: .center) {
Rectangle()
.frame(height: 0)
.background(Color(hex: "#F2F2F2"), ignoresSafeAreaEdges: .top)
.border(.red)
Group {
if followModel.dramas.count > 0 {
ScrollView(.vertical, showsIndicators: false) {
LazyVStack(alignment: .center) {
ForEach(followModel.dramas, id: \.id) { drama in
DramaCellView(dramaItem: drama)
}
}
}
} else {
VStack {
Spacer()
Text("这里什么都没有")
.font(.system(size: 16))
.foregroundColor(.black)
Spacer()
}
}
}
.frame(width: 370)
}
.ignoresSafeArea(edges: .bottom)
.navigationTitle("番剧补完计划")
.task {
await self.followModel.loadData(userId: self.userId)
}
}
}
extension FollowListView {
//
struct DramaCellView: View {
let dramaItem: FollowListModel.DramaItem
var body: some View {
VStack(alignment: .leading) {
NavigationLink(destination: DetailView(id: dramaItem.id)) {
Text(dramaItem.title)
.font(.system(size: 20))
.foregroundColor(Color(hex: "#333333"))
.lineLimit(1)
}
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(alignment: .center, spacing: 5) {
ForEach(dramaItem.episodes) { item in
VStack(alignment: .center) {
GeometryReader { geometry in
AsyncImage(url: URL(string: item.thumb)) { phase in
switch phase {
case .empty:
ProgressView()
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geometry.frame(in: .local).width, height: 80)
.clipped()
default:
Image("ph_img_medium")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geometry.frame(in: .local).width, height: 80)
.clipped()
}
}
.frame(width: geometry.frame(in: .local).width, height: 80)
.overlay(alignment: .topLeading) {
if !item.num_name.isEmpty {
HStack(alignment: .center) {
Text(item.num_name)
.font(.system(size: 12))
.foregroundColor(.white)
.lineLimit(1)
}
.padding(3)
.background(
Color.black.opacity(0.6)
)
.cornerRadius(3)
.padding(3)
} else {
EmptyView()
}
}
}
Text(item.name)
.font(.system(size: 12))
.foregroundColor(Color(hex: "#333333"))
.lineLimit(1)
}
.frame(width: 120, height: 100)
.onTapGesture {
if let playUrl = URL(string: item.play) {
UIApplication.shared.open(playUrl)
}
}
}
}
}
}
}
}
}
#Preview {
FollowListView()
}

View File

@ -0,0 +1,72 @@
//
// ListModel.swift
// dimensionhub
//
// Created by on 2025/4/8.
//
import Foundation
import Observation
@Observable
final class ListModel {
struct Episode: Codable, Identifiable {
let id = UUID().uuidString
let name: String
let num_name: String
let thumb: String
let play: String
enum CodingKeys: String, CodingKey {
case name, num_name, thumb, play
}
}
//
struct Channel: Codable {
let name: String
let episodes: [Episode]
}
struct DramaDetailResponse: Codable {
let name: String
let summary: String
let thumb: String
let status: [String]
let channels: [Channel]
}
var name: String = ""
var summary: String = ""
var thumb: String = ""
var channels: [Channel] = []
// channel
var selectedChannelIdx: Int = 0
var selectedEpisodes: [Episode] = []
func loadData(userId: String, id: Int) async {
let response = await API.getDramaDetail(userId: userId, id: id, as: DramaDetailResponse.self)
switch response {
case .error(let code, let message):
print(code)
print(message)
case .result(let detail):
await MainActor.run {
self.name = detail.name
self.summary = Utils.converHtmlToString(html: detail.summary) ?? ""
self.thumb = detail.thumb
self.channels = detail.channels
self.selectedChannelIdx = 0
self.selectedEpisodes = detail.channels[0].episodes
}
}
}
func toggleChannel(channelIdx: Int) {
self.selectedChannelIdx = channelIdx
self.selectedEpisodes = self.channels[channelIdx].episodes
}
}

View File

@ -5,77 +5,11 @@
// Created by on 2025/2/21. // Created by on 2025/2/21.
// //
import SwiftUI import SwiftUI
import Observation
@Observable
final class ListModel {
struct Episode: Codable, Identifiable {
let id = UUID().uuidString
let name: String
let num_name: String
let thumb: String
let play: String
enum CodingKeys: String, CodingKey {
case name, num_name, thumb, play
}
}
//
struct Channel: Codable {
let name: String
let episodes: [Episode]
}
struct DramaDetailResponse: Codable {
let name: String
let summary: String
let thumb: String
let status: [String]
let channels: [Channel]
}
var name: String = ""
var summary: String = ""
var thumb: String = ""
var channels: [Channel] = []
// channel
var selectedChannelIdx: Int = 0
var selectedEpisodes: [Episode] = []
func loadData(userId: String, id: Int) async {
let response = await API.getDramaDetail(userId: userId, id: id, as: DramaDetailResponse.self)
switch response {
case .error(let code, let message):
print(code)
print(message)
case .result(let detail):
await MainActor.run {
self.name = detail.name
self.summary = Utils.converHtmlToString(html: detail.summary) ?? ""
self.thumb = detail.thumb
self.channels = detail.channels
self.selectedChannelIdx = 0
self.selectedEpisodes = detail.channels[0].episodes
}
}
}
func toggleChannel(channelIdx: Int) {
self.selectedChannelIdx = channelIdx
self.selectedEpisodes = self.channels[channelIdx].episodes
}
}
struct ListView: View { struct ListView: View {
@Environment(\.presentationMode) var presentationMode
@AppStorage("userId") private var userId: String = Utils.defaultUserId() @AppStorage("userId") private var userId: String = Utils.defaultUserId()
@State var detailModel = ListModel() @State var detailModel = ListModel()
let id: Int let id: Int
var body: some View { var body: some View {
@ -86,7 +20,7 @@ struct ListView: View {
.padding([.leading, .top, .bottom], 10) .padding([.leading, .top, .bottom], 10)
.background(Color(hex: "#F2F2F2"), ignoresSafeAreaEdges: [.top]) .background(Color(hex: "#F2F2F2"), ignoresSafeAreaEdges: [.top])
VStack { VStack(alignment: .center) {
// //
HStack(alignment: .center, spacing: 15) { HStack(alignment: .center, spacing: 15) {
ForEach(Array(detailModel.channels.enumerated()), id: \.offset) { idx, channel in ForEach(Array(detailModel.channels.enumerated()), id: \.offset) { idx, channel in
@ -109,22 +43,6 @@ struct ListView: View {
} }
} }
// HStack(alignment: .center) {
// Button {
// self.presentationMode.wrappedValue.dismiss()
// } label: {
// Rectangle()
// .frame(width: 200, height: 25)
// .foregroundColor(Color(hex: "#F2F2F2"))
// .overlay {
// Text("")
// .font(.system(size: 13))
// .foregroundColor(Color(hex: "#999999"))
// }
// }
//
// }
Spacer() Spacer()
} }
.frame(width: 370, alignment: .center) .frame(width: 370, alignment: .center)