Commit 55ceadf2 authored by Alex朱枝文's avatar Alex朱枝文

我的资源

parent ebbf1004
......@@ -72,6 +72,10 @@
04307BB42D21623300ED8E8D /* YHRecommendedAppointmentLiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04307BB32D21623300ED8E8D /* YHRecommendedAppointmentLiveView.swift */; };
04307BB62D2180C300ED8E8D /* YHRecommendedOnLiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04307BB52D2180C300ED8E8D /* YHRecommendedOnLiveView.swift */; };
04307BBC2D22A21E00ED8E8D /* YHLiveStatusModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04307BBB2D22A21E00ED8E8D /* YHLiveStatusModel.swift */; };
04355C8D2E890DF10083BE91 /* YHResourceViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04355C8C2E890DF10083BE91 /* YHResourceViewModel.swift */; };
04355C912E890E020083BE91 /* YHResourceCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04355C8F2E890E020083BE91 /* YHResourceCategoryView.swift */; };
04355C932E8912490083BE91 /* YHResourceTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04355C922E8912490083BE91 /* YHResourceTableViewCell.swift */; };
04355C952E8912620083BE91 /* YHResourceListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04355C942E8912620083BE91 /* YHResourceListModel.swift */; };
0436713B2D643A7A00E8D9BA /* YHMemberCenterMyPrivilegeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0436713A2D643A7A00E8D9BA /* YHMemberCenterMyPrivilegeCell.swift */; };
0436713D2D6470A400E8D9BA /* YHMemberCenterActionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0436713C2D6470A400E8D9BA /* YHMemberCenterActionCell.swift */; };
0436713F2D64817500E8D9BA /* YHMemberCenterFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0436713E2D64817500E8D9BA /* YHMemberCenterFooterView.swift */; };
......@@ -1175,6 +1179,7 @@
047A968F2D16AA410033BB4E /* YHGCIncomeRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047A968A2D16AA410033BB4E /* YHGCIncomeRecordViewController.swift */; };
047A96902D16AA410033BB4E /* YHGCIncomeRecordWorkExperienceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047A968B2D16AA410033BB4E /* YHGCIncomeRecordWorkExperienceViewController.swift */; };
047A96932D16C0900033BB4E /* YHInfoQuestionSelectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047A96922D16C0900033BB4E /* YHInfoQuestionSelectionCell.swift */; };
0487C1C72E8940D000CE6472 /* YHCustomSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0487C1C62E8940D000CE6472 /* YHCustomSearchView.swift */; };
048D6ADB2D5E00DE00BC6F4C /* YHMemberCenterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048D6ADA2D5E00DE00BC6F4C /* YHMemberCenterViewController.swift */; };
048D6ADD2D5EF0A900BC6F4C /* YHMemberCenterHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048D6ADC2D5EF0A900BC6F4C /* YHMemberCenterHeaderView.swift */; };
048D6ADF2D5F0FBE00BC6F4C /* YHMemberCenterHeaderBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048D6ADE2D5F0FBE00BC6F4C /* YHMemberCenterHeaderBannerCell.swift */; };
......@@ -1433,6 +1438,10 @@
04307BB32D21623300ED8E8D /* YHRecommendedAppointmentLiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHRecommendedAppointmentLiveView.swift; sourceTree = "<group>"; };
04307BB52D2180C300ED8E8D /* YHRecommendedOnLiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHRecommendedOnLiveView.swift; sourceTree = "<group>"; };
04307BBB2D22A21E00ED8E8D /* YHLiveStatusModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHLiveStatusModel.swift; sourceTree = "<group>"; };
04355C8C2E890DF10083BE91 /* YHResourceViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHResourceViewModel.swift; sourceTree = "<group>"; };
04355C8F2E890E020083BE91 /* YHResourceCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHResourceCategoryView.swift; sourceTree = "<group>"; };
04355C922E8912490083BE91 /* YHResourceTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHResourceTableViewCell.swift; sourceTree = "<group>"; };
04355C942E8912620083BE91 /* YHResourceListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHResourceListModel.swift; sourceTree = "<group>"; };
0436713A2D643A7A00E8D9BA /* YHMemberCenterMyPrivilegeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMemberCenterMyPrivilegeCell.swift; sourceTree = "<group>"; };
0436713C2D6470A400E8D9BA /* YHMemberCenterActionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMemberCenterActionCell.swift; sourceTree = "<group>"; };
0436713E2D64817500E8D9BA /* YHMemberCenterFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMemberCenterFooterView.swift; sourceTree = "<group>"; };
......@@ -2540,6 +2549,7 @@
047A968A2D16AA410033BB4E /* YHGCIncomeRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHGCIncomeRecordViewController.swift; sourceTree = "<group>"; };
047A968B2D16AA410033BB4E /* YHGCIncomeRecordWorkExperienceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHGCIncomeRecordWorkExperienceViewController.swift; sourceTree = "<group>"; };
047A96922D16C0900033BB4E /* YHInfoQuestionSelectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHInfoQuestionSelectionCell.swift; sourceTree = "<group>"; };
0487C1C62E8940D000CE6472 /* YHCustomSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHCustomSearchView.swift; sourceTree = "<group>"; };
048D6ADA2D5E00DE00BC6F4C /* YHMemberCenterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMemberCenterViewController.swift; sourceTree = "<group>"; };
048D6ADC2D5EF0A900BC6F4C /* YHMemberCenterHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMemberCenterHeaderView.swift; sourceTree = "<group>"; };
048D6ADE2D5F0FBE00BC6F4C /* YHMemberCenterHeaderBannerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMemberCenterHeaderBannerCell.swift; sourceTree = "<group>"; };
......@@ -7031,6 +7041,7 @@
04D4EC2D2E839B4200B0329B /* M */ = {
isa = PBXGroup;
children = (
04355C942E8912620083BE91 /* YHResourceListModel.swift */,
);
path = M;
sourceTree = "<group>";
......@@ -7038,6 +7049,9 @@
04D4EC2E2E839B4200B0329B /* V */ = {
isa = PBXGroup;
children = (
0487C1C62E8940D000CE6472 /* YHCustomSearchView.swift */,
04355C922E8912490083BE91 /* YHResourceTableViewCell.swift */,
04355C8F2E890E020083BE91 /* YHResourceCategoryView.swift */,
);
path = V;
sourceTree = "<group>";
......@@ -7045,6 +7059,7 @@
04D4EC2F2E839B4200B0329B /* VM */ = {
isa = PBXGroup;
children = (
04355C8C2E890DF10083BE91 /* YHResourceViewModel.swift */,
);
path = VM;
sourceTree = "<group>";
......@@ -7702,6 +7717,7 @@
045C101A2D12CA5F00BD2DC0 /* YHActivityDetailCell1.swift in Sources */,
045C101B2D12CA5F00BD2DC0 /* YHLoginOneLeadView.swift in Sources */,
0411CEE22D14014000644D35 /* YHGCMainInformationCardTableViewCell.swift in Sources */,
04355C8D2E890DF10083BE91 /* YHResourceViewModel.swift in Sources */,
045C101C2D12CA5F00BD2DC0 /* YHCollectionViewLeftAlignedFlowLayout.swift in Sources */,
0411CF082D1A896800644D35 /* YHGCMySignatureListModel.swift in Sources */,
045C101D2D12CA5F00BD2DC0 /* YHHUDProgressView.swift in Sources */,
......@@ -7984,6 +8000,7 @@
045C11072D12CA5F00BD2DC0 /* YHIncomeItemOccupyingCell.swift in Sources */,
045C11082D12CA5F00BD2DC0 /* YHServerCenterHoldViewController.swift in Sources */,
045C11092D12CA5F00BD2DC0 /* YHResignCertificateListViewModel.swift in Sources */,
0487C1C72E8940D000CE6472 /* YHCustomSearchView.swift in Sources */,
045C110A2D12CA5F00BD2DC0 /* YHResignDocumentListModel.swift in Sources */,
04307BB62D2180C300ED8E8D /* YHRecommendedOnLiveView.swift in Sources */,
0436713F2D64817500E8D9BA /* YHMemberCenterFooterView.swift in Sources */,
......@@ -8042,6 +8059,7 @@
045C11372D12CA5F00BD2DC0 /* YHButlerServiceManager.swift in Sources */,
045C11382D12CA5F00BD2DC0 /* YHHomeModel.swift in Sources */,
045C11392D12CA5F00BD2DC0 /* YHSchemeHeadScoreItemView.swift in Sources */,
04355C912E890E020083BE91 /* YHResourceCategoryView.swift in Sources */,
045C113A2D12CA5F00BD2DC0 /* YHAIViewModel.swift in Sources */,
045C113B2D12CA5F00BD2DC0 /* YHButlerServiceMessageHandler.swift in Sources */,
045C113C2D12CA5F00BD2DC0 /* YHResignScheduleLineCollectCell.swift in Sources */,
......@@ -8443,6 +8461,7 @@
045C12732D12CA5F00BD2DC0 /* YHActivityDetailItemView.swift in Sources */,
045C12742D12CA5F00BD2DC0 /* YHWorkExperienceListViewController.swift in Sources */,
045C12752D12CA5F00BD2DC0 /* YHEducationInfo.swift in Sources */,
04355C952E8912620083BE91 /* YHResourceListModel.swift in Sources */,
045C12762D12CA5F00BD2DC0 /* UIFont+Extension.swift in Sources */,
045C12772D12CA5F00BD2DC0 /* YHTabBarViewController.swift in Sources */,
045C12782D12CA5F00BD2DC0 /* YHHKRecordsPersonnelSelectCell.swift in Sources */,
......@@ -8558,6 +8577,7 @@
045647572D7976880001D831 /* YHHKVisaRenewalApplicationVC.swift in Sources */,
045C12D92D12CA5F00BD2DC0 /* YHPayMemberSectionItem.swift in Sources */,
045C12DA2D12CA5F00BD2DC0 /* YHPickTimeHoldView.swift in Sources */,
04355C932E8912490083BE91 /* YHResourceTableViewCell.swift in Sources */,
04307B9F2D1D4B3600ED8E8D /* YHGCIncomeTypeSelectViewController.swift in Sources */,
045C12DB2D12CA5F00BD2DC0 /* YHServiceOrderInIncubationView.swift in Sources */,
045C12DC2D12CA5F00BD2DC0 /* YHUatHelperViewModel.swift in Sources */,
......
......@@ -41,9 +41,9 @@ class YHTencentDeskManager: NSObject {
/// 需要隐藏已读未读
TUIConfig.default().needHideReadReceipt = true
/// 需要隐藏聊天页点击用户头像显示用户信息
TUIChatConfig.default().needHideShowUserInfoPage = true
TUIChatConfig.default().needHideShowUserInfoPage = false
/// 需要隐藏显示聊天页rightBarButton按钮
TUIChatConfig.default().needHideNaviMore = true
TUIChatConfig.default().needHideNaviMore = false
}
func loginSDK() {
......
//
// YHCircleViewModel.swift
// galaxy
//
// Created by alexzzw on 2025/9/24.
// Copyright © 2025 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHCircleViewModel: NSObject {
}
......@@ -3,21 +3,581 @@
// galaxy
//
// Created by alexzzw on 2025/9/24.
// Copyright © 2025 https://www.galaxy-immi.com. All rights reserved.
// Copyright © 2025 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import ESPullToRefresh
import JXSegmentedView
import SnapKit
class YHResourceViewController: YHBaseViewController {
// 添加高度约束属性
private var categoryViewHeightConstraint: Constraint?
lazy var viewModel: YHResourceViewModel = {
let viewModel = YHResourceViewModel()
return viewModel
}()
// 筛选条件
var selectedCategories: [YHResourceCategory] = []
// MARK: - UI Components
// 自定义搜索视图
lazy var customSearchView: YHCustomSearchView = {
let searchView = YHCustomSearchView()
searchView.delegate = self
searchView.placeholder = "搜索企业名称、企业供需"
return searchView
}()
// 筛选按钮容器
lazy var filterContainerView: UIView = {
let view = UIView()
view.backgroundColor = .white
return view
}()
// 企业服务按钮
lazy var serviceButton: UIButton = {
let btn = UIButton(type: .custom)
btn.setTitle("企业服务", for: .normal)
btn.setTitleColor(UIColor.brandGrayColor7, for: .normal)
btn.setTitleColor(UIColor.brandGrayColor8, for: .selected)
btn.titleLabel?.font = UIFont.PFSC_R(ofSize: 13)
btn.layer.borderWidth = 1
btn.layer.borderColor = UIColor.brandGrayColor4.cgColor
btn.addTarget(self, action: #selector(serviceButtonClicked), for: .touchUpInside)
return btn
}()
// 企业需求按钮
lazy var demandButton: UIButton = {
let btn = UIButton(type: .custom)
btn.setTitle("企业需求", for: .normal)
btn.setTitleColor(UIColor.brandGrayColor7, for: .normal)
btn.setTitleColor(UIColor.brandGrayColor8, for: .selected)
btn.titleLabel?.font = UIFont.PFSC_R(ofSize: 13)
btn.layer.borderWidth = 1
btn.layer.borderColor = UIColor.brandGrayColor4.cgColor
btn.addTarget(self, action: #selector(demandButtonClicked), for: .touchUpInside)
return btn
}()
// 全部行业按钮
lazy var industryButton: UIButton = {
let btn = UIButton(type: .custom)
btn.setTitle("行业", for: .normal)
btn.setTitleColor(UIColor.brandGrayColor7, for: .normal)
btn.titleLabel?.font = UIFont.PFSC_R(ofSize: 13)
btn.setImage(UIImage(named: "resource_filter_down"), for: .normal)
btn.addTarget(self, action: #selector(industryButtonClicked), for: .touchUpInside)
return btn
}()
// 行业分类视图
lazy var categoryView: YHResourceCategoryView = {
let view = YHResourceCategoryView()
view.backgroundColor = .white
view.isHidden = true
view.delegate = self
return view
}()
// 遮罩视图
lazy var maskView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.black.withAlphaComponent(0.3)
view.isHidden = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideCategoryView))
view.addGestureRecognizer(tapGesture)
return view
}()
// 资源列表
lazy var resourceTableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .plain)
tableView.backgroundColor = UIColor.systemGray6
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .none
tableView.register(YHResourceTableViewCell.self, forCellReuseIdentifier: YHResourceTableViewCell.cellReuseIdentifier)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.showsVerticalScrollIndicator = false
return tableView
}()
// 空状态视图
private lazy var noDataView: YHEmptyDataView = {
let view = YHEmptyDataView.createView("暂无内容", kEmptyCommonBgName)
view.frame = CGRect(x: 0, y: 0, width: KScreenWidth, height: 164)
view.backgroundColor = .clear
view.isHidden = true
return view
}()
// 发布需求按钮
lazy var publishButton: UIButton = {
let btn = UIButton(type: .custom)
btn.backgroundColor = UIColor.brandGrayColor8
btn.setImage(UIImage(named: "circle_publish_icon"), for: .normal)
btn.setTitle("发布需求", for: .normal)
btn.setTitleColor(UIColor.white, for: .normal)
btn.titleLabel?.font = UIFont.PFSC_R(ofSize: 15)
btn.layer.cornerRadius = 22
btn.addTarget(self, action: #selector(publishButtonClicked), for: .touchUpInside)
return btn
}()
/*
// MARK: - Navigation
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
addDefaultData()
getData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if viewModel.arrResourceData?.isEmpty == true {
getData()
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
// MARK: - 私有方法
private extension YHResourceViewController {
func setupUI() {
gk_navigationBar.isHidden = true
view.backgroundColor = UIColor.contentBkgColor
// 添加子视图
view.addSubview(customSearchView)
view.addSubview(filterContainerView)
filterContainerView.addSubview(serviceButton)
filterContainerView.addSubview(demandButton)
filterContainerView.addSubview(industryButton)
view.addSubview(resourceTableView)
view.addSubview(noDataView)
view.addSubview(publishButton)
// 分类视图要最后添加,确保在最上层
view.addSubview(maskView)
view.addSubview(categoryView)
setupConstraints()
setupFilterButtons()
setupPullToRefresh()
}
func setupConstraints() {
customSearchView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
make.height.equalTo(60)
}
filterContainerView.snp.makeConstraints { make in
make.top.equalTo(customSearchView.snp.bottom)
make.left.right.equalToSuperview()
make.height.equalTo(44)
}
serviceButton.setContentCompressionResistancePriority(.required, for: .horizontal)
demandButton.setContentCompressionResistancePriority(.required, for: .horizontal)
industryButton.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
serviceButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(20)
make.centerY.equalToSuperview()
make.width.equalTo(76)
make.height.equalTo(26)
}
demandButton.snp.makeConstraints { make in
make.left.equalTo(serviceButton.snp.right).offset(10)
make.centerY.equalToSuperview()
make.width.equalTo(76)
make.height.equalTo(26)
}
industryButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-20)
make.centerY.equalToSuperview()
make.width.greaterThanOrEqualTo(32)
make.left.greaterThanOrEqualTo(demandButton.snp.right).offset(8)
make.height.equalTo(26)
}
categoryView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalTo(filterContainerView.snp.bottom)
// 使用变量保存高度约束的引用
self.categoryViewHeightConstraint = make.height.equalTo(0).constraint
}
maskView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalTo(filterContainerView.snp.bottom)
}
resourceTableView.snp.makeConstraints { make in
make.top.equalTo(filterContainerView.snp.bottom)
make.left.right.bottom.equalToSuperview()
}
noDataView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.centerY.equalToSuperview()
make.width.equalTo(KScreenWidth)
make.height.equalTo(164)
}
publishButton.snp.makeConstraints { make in
make.width.equalTo(134)
make.height.equalTo(44)
make.centerX.equalToSuperview()
make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-30)
}
publishButton.iconInLeft(spacing: 6)
industryButton.iconInRight(with: 6)
}
func setupFilterButtons() {
// 默认选中企业服务
selectFilterType(.service)
}
func setupPullToRefresh() {
resourceTableView.es.addYHPullToRefresh {
self.getData()
}
resourceTableView.es.addInfiniteScrolling {
self.loadMoreData()
}
resourceTableView.es.stopPullToRefresh()
resourceTableView.es.stopLoadingMore()
}
func addDefaultData() {
getCacheDataForResourceData()
}
func getCacheDataForResourceData() {
let arrString = UserDefaults.standard.value(forKey: "resourceFirstPageData")
if let tmp = arrString as? String {
guard let result = [YHResourceListModel].deserialize(from: tmp) else {
return
}
if !result.isEmpty {
// 计算每个模型的高度参数
for item in result {
item.calHeightParam()
}
self.viewModel.arrResourceData = result
self.updateUI()
}
}
}
func getData() {
loadFirstData()
}
func loadFirstData() {
// 显示loading状态
if viewModel.arrResourceData?.isEmpty ?? true {
YHHUD.show(.progress(message: "加载中..."))
}
DispatchQueue.global().async {
self.viewModel.getResourceList(firstFlag: true) { [weak self] success, message in
guard let self = self else { return }
DispatchQueue.main.async {
YHHUD.hide()
if success, let arrData = self.viewModel.arrResourceData, arrData.count > 0 {
let arrString = arrData.toJSONString()
UserDefaults.standard.set(arrString, forKey: "resourceFirstPageData")
UserDefaults.standard.synchronize()
} else if !success, let errorMessage = message {
YHHUD.flash(message: errorMessage)
}
self.resourceTableView.es.stopPullToRefresh()
if self.viewModel.hasMoreForResource == false {
self.resourceTableView.es.noticeNoMoreData()
self.resourceTableView.footer?.alpha = 1
}
self.updateUI()
}
}
}
}
func loadMoreData() {
DispatchQueue.global().async {
self.viewModel.getResourceList(firstFlag: false) { [weak self] _, _ in
guard let self = self else { return }
DispatchQueue.main.asyncAfter(wallDeadline: .now() + 0.35, execute: {
self.resourceTableView.reloadData()
self.resourceTableView.es.stopLoadingMore()
if self.viewModel.hasMoreForResource == false {
self.resourceTableView.es.noticeNoMoreData()
self.resourceTableView.footer?.alpha = 1
}
})
}
}
}
func updateUI() {
let hasData = viewModel.arrResourceData?.count ?? 0 > 0
resourceTableView.isHidden = !hasData
noDataView.isHidden = hasData
if hasData {
resourceTableView.reloadData()
}
}
func updateIndustryButtonTitle() {
if selectedCategories.isEmpty {
industryButton.setTitle("行业", for: .normal)
industryButton.setTitleColor(UIColor.brandGrayColor7, for: .normal)
industryButton.titleLabel?.font = UIFont.PFSC_R(ofSize: 13)
} else if selectedCategories.count == 1 {
industryButton.setTitle(selectedCategories.first?.name ?? "全部行业", for: .normal)
industryButton.setTitleColor(UIColor.brandGrayColor8, for: .normal)
industryButton.titleLabel?.font = UIFont.PFSC_M(ofSize: 13)
} else {
industryButton.setTitle("已选\(selectedCategories.count)个行业", for: .normal)
industryButton.setTitleColor(UIColor.brandGrayColor8, for: .normal)
industryButton.titleLabel?.font = UIFont.PFSC_M(ofSize: 13)
}
industryButton.iconInRight(with: 6)
}
func resetCategorySelection() {
selectedCategories.removeAll()
viewModel.selectedCategories.removeAll()
updateIndustryButtonTitle()
}
func resetAllFilters() {
// 重置所有筛选条件
customSearchView.clearText()
viewModel.searchKeyword = nil
resetCategorySelection()
getData()
}
// MARK: - 按钮事件处理
@objc func serviceButtonClicked() {
selectFilterType(.service)
viewModel.currentType = .service
// 重置分类选择
resetCategorySelection()
getData()
}
@objc func demandButtonClicked() {
selectFilterType(.demand)
viewModel.currentType = .demand
// 重置分类选择
resetCategorySelection()
getData()
}
@objc func industryButtonClicked() {
if categoryView.isHidden {
showCategoryView()
} else {
hideCategoryView()
}
}
func showCategoryView() {
// 设置当前选中的分类
categoryView.setSelectedCategories(selectedCategories)
// 显示遮罩和分类视图
maskView.isHidden = false
categoryView.isHidden = false
maskView.alpha = 0
// 目标高度(可以根据实际内容调整)
let targetHeight: CGFloat = UIScreen.main.bounds.width
// 高度约束动画
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut, animations: {
// 更新高度约束
self.categoryViewHeightConstraint?.update(offset: targetHeight)
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
// 更新遮罩透明度
self.maskView.alpha = 1
// 触发布局更新
self.view.layoutIfNeeded()
})
// 更新按钮状态
industryButton.setImage(UIImage(named: "resource_filter_up"), for: .normal)
}
*/
// 修改隐藏动画方法
@objc func hideCategoryView() {
// 高度约束动画
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn, animations: {
// 更新高度约束为0
self.categoryViewHeightConstraint?.update(offset: 0)
// 更新遮罩透明度
self.maskView.alpha = 0
// 触发布局更新
self.view.layoutIfNeeded()
}) { _ in
// 动画完成后隐藏视图
self.categoryView.isHidden = true
self.maskView.isHidden = true
}
// 更新按钮状态
industryButton.setImage(UIImage(named: "resource_filter_down"), for: .normal)
}
@objc func publishButtonClicked() {
if !YHLoginManager.shared.isLogin() {
YHOneKeyLoginManager.shared.oneKeyLogin()
return
}
//
}
func selectFilterType(_ type: YHResourceFilterType) {
// 重置所有按钮状态
[serviceButton, demandButton].forEach { btn in
btn.isSelected = false
btn.layer.borderColor = UIColor.brandGrayColor4.cgColor
}
// 设置选中状态
let selectedButton = type == .service ? serviceButton : demandButton
selectedButton.isSelected = true
selectedButton.layer.borderColor = UIColor.brandGrayColor8.cgColor
}
}
// MARK: - YHCustomSearchViewDelegate
extension YHResourceViewController: YHCustomSearchViewDelegate {
func searchView(_ searchView: YHCustomSearchView, didSearchWithText text: String?) {
viewModel.searchKeyword = text
if text?.isEmpty == true {
getData()
}
}
func searchViewDidBeginEditing(_ searchView: YHCustomSearchView) {
// 搜索开始编辑时的处理
}
func searchViewDidEndEditing(_ searchView: YHCustomSearchView) {
// 搜索结束编辑时的处理
getData()
}
}
// MARK: - UITableViewDelegate, UITableViewDataSource
extension YHResourceViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.arrResourceData?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let datas = viewModel.arrResourceData,
datas.count > indexPath.row,
let cell = tableView.dequeueReusableCell(withIdentifier: YHResourceTableViewCell.cellReuseIdentifier, for: indexPath) as? YHResourceTableViewCell else {
return UITableViewCell()
}
cell.resourceModel = datas[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
guard let model = viewModel.arrResourceData?[indexPath.row] else { return }
//
}
}
// MARK: - UIScrollViewDelegate
extension YHResourceViewController {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let itemNumber = viewModel.preloadItemIndex
let visibleRows = resourceTableView.indexPathsForVisibleRows ?? []
let lastVisibleRow = visibleRows.last?.row ?? 0
if lastVisibleRow >= itemNumber - 1 {
loadMoreData()
}
}
}
// MARK: - JXSegmentedListContainerViewListDelegate
extension YHResourceViewController: JXSegmentedListContainerViewListDelegate {
func listView() -> UIView {
return view
}
}
// MARK: - YHResourceCategoryViewDelegate
extension YHResourceViewController: YHResourceCategoryViewDelegate {
func categoryView(_ view: YHResourceCategoryView, didSelectCategories categories: [YHResourceCategory], isSet: Bool) {
selectedCategories = categories
viewModel.selectedCategories = categories
updateIndustryButtonTitle()
if isSet {
// 隐藏分类视图
hideCategoryView()
}
// 重新加载数据
getData()
}
}
// MARK: - 枚举定义
enum YHResourceFilterType {
case service
case demand
}
//
// YHResourceListModel.swift
// galaxy
//
// Created by alexzzw on 2025/9/24.
// Copyright © 2025 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import SmartCodable
// MARK: - 资源列表模型
class YHResourceListModel: SmartCodable {
required init() {}
// MARK: - 基本信息
var id: String = ""
var title: String = ""
var content: String = ""
var company_name: String = ""
var company_logo: String = ""
var contact_name: String = ""
var contact_phone: String = ""
var contact_wechat: String = ""
var category_id: String = ""
var category_name: String = ""
var type: String = "" // service 或 demand
var status: Int = 0 // 0-待审核 1-已发布 2-已下架
var is_favorite: Bool = false
var favorite_count: Int = 0
var view_count: Int = 0
var created_time: String = ""
var updated_time: String = ""
var expires_time: String = ""
var images: [String] = []
var tags: [String] = []
var location: String = ""
var price: String = ""
var price_unit: String = ""
// MARK: - 扩展属性
var user_id: String = ""
var user_name: String = ""
var user_avatar: String = ""
var certification_status: Int = 0 // 0-未认证 1-已认证
var priority: Int = 0 // 优先级,用于排序
var click_count: Int = 0 // 点击量
var comment_count: Int = 0 // 评论数
var share_count: Int = 0 // 分享数
var service_area: String = "" // 服务区域
var min_price: String = "" // 最低价格
var max_price: String = "" // 最高价格
var service_duration: String = "" // 服务周期
var qualification: String = "" // 资质证明
var business_hours: String = "" // 营业时间
var website: String = "" // 公司网站
var email: String = "" // 邮箱
var address: String = "" // 详细地址
// MARK: - UI相关属性
var cell_width: CGFloat = 0
var cell_height: CGFloat = 0
// MARK: - 计算属性
/// 显示时间(相对时间格式)
var displayTime: String {
return created_time.toDisplayTime()
}
/// 显示价格
var displayPrice: String {
if price.isEmpty {
if !min_price.isEmpty && !max_price.isEmpty {
return "\(min_price)-\(max_price)" + (price_unit.isEmpty ? "" : "/" + price_unit)
} else if !min_price.isEmpty {
return "¥\(min_price)起" + (price_unit.isEmpty ? "" : "/" + price_unit)
}
return "面议"
}
return "¥" + price + (price_unit.isEmpty ? "" : "/" + price_unit)
}
/// 类型显示名称
var typeDisplayName: String {
return type == "service" ? "企业服务" : "企业需求"
}
/// 状态显示名称
var statusDisplayName: String {
switch status {
case 0:
return "待审核"
case 1:
return "已发布"
case 2:
return "已下架"
default:
return "未知"
}
}
/// 认证状态显示
var certificationDisplayName: String {
return certification_status == 1 ? "已认证" : "未认证"
}
/// 是否已认证
var isCertified: Bool {
return certification_status == 1
}
/// 是否为服务类型
var isService: Bool {
return type == "service"
}
/// 是否为需求类型
var isDemand: Bool {
return type == "demand"
}
/// 是否已过期
var isExpired: Bool {
guard !expires_time.isEmpty else { return false }
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
guard let expireDate = formatter.date(from: expires_time) else { return false }
return Date() > expireDate
}
/// 剩余天数
var remainingDays: Int {
guard !expires_time.isEmpty else { return -1 }
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
guard let expireDate = formatter.date(from: expires_time) else { return -1 }
let calendar = Calendar.current
let components = calendar.dateComponents([.day], from: Date(), to: expireDate)
return components.day ?? -1
}
/// 热度值(综合点击、收藏、分享等)
var hotScore: Int {
return view_count + favorite_count * 2 + share_count * 3 + comment_count * 1
}
/// 第一张图片URL
var firstImageUrl: String {
return images.first ?? ""
}
/// 是否有图片
var hasImages: Bool {
return !images.isEmpty
}
/// 标签字符串(用逗号分隔)
var tagsString: String {
return tags.joined(separator: ", ")
}
// MARK: - 计算Cell高度方法
func calHeightParam() {
let screenWidth = KScreenWidth
let cellWidth = (screenWidth - 47) / 2
var totalHeight: CGFloat = 60 + 20 + 44 // 基础高度
// 标题高度
if !title.isEmpty {
let titleFont = UIFont.PFSC_M(ofSize: 16) ?? UIFont.systemFont(ofSize: 16)
let maxSize = CGSize(width: cellWidth - 24, height: CGFloat.greatestFiniteMagnitude)
let titleRect = title.boundingRect(
with: maxSize,
options: [.usesLineFragmentOrigin, .usesFontLeading],
attributes: [.font: titleFont],
context: nil
)
totalHeight += ceil(titleRect.height) + 8
}
// 内容高度
if !content.isEmpty {
let contentFont = UIFont.PFSC_R(ofSize: 13) ?? UIFont.systemFont(ofSize: 13)
let maxSize = CGSize(width: cellWidth - 24, height: CGFloat.greatestFiniteMagnitude)
let textRect = content.boundingRect(
with: maxSize,
options: [.usesLineFragmentOrigin, .usesFontLeading],
attributes: [.font: contentFont],
context: nil
)
totalHeight += min(ceil(textRect.height), 40) + 8 // 限制最大高度为2行
}
// 图片高度
if !images.isEmpty {
let imageHeight: CGFloat = 100
totalHeight += imageHeight + 8
}
// 标签高度
if !tags.isEmpty {
totalHeight += 20 + 8
}
// 底部信息高度
totalHeight += 30
self.cell_width = cellWidth
self.cell_height = max(totalHeight, 160) // 最小高度160
}
// MARK: - 便利方法
/// 更新收藏状态
func updateFavoriteStatus(_ isFavorite: Bool) {
is_favorite = isFavorite
if isFavorite {
favorite_count += 1
} else {
favorite_count = max(0, favorite_count - 1)
}
}
/// 增加浏览量
func incrementViewCount() {
view_count += 1
}
/// 增加分享数
func incrementShareCount() {
share_count += 1
}
/// 验证数据完整性
func isValid() -> Bool {
return !id.isEmpty &&
!title.isEmpty &&
!content.isEmpty &&
!company_name.isEmpty &&
!contact_name.isEmpty &&
!contact_phone.isEmpty &&
!category_id.isEmpty
}
/// 获取联系方式字符串
func getContactInfo() -> String {
var contactInfo = contact_name
if !contact_phone.isEmpty {
contactInfo += " \(contact_phone)"
}
if !contact_wechat.isEmpty {
contactInfo += " 微信:\(contact_wechat)"
}
return contactInfo
}
}
// MARK: - 资源分类模型
class YHResourceCategory: SmartCodable {
required init() {}
var id: String = ""
var name: String = ""
var icon: String = ""
var sort: Int = 0
var parent_id: String = ""
var children: [YHResourceCategory] = []
var description_text: String = ""
var is_hot: Bool = false
var resource_count: Int = 0
// 计算属性
var hasChildren: Bool {
return !children.isEmpty
}
var isTopLevel: Bool {
return parent_id.isEmpty
}
}
// MARK: - String Extension
extension String {
func toDisplayTime() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
guard let date = formatter.date(from: self) else {
return self
}
let now = Date()
let interval = now.timeIntervalSince(date)
if interval < 60 {
return "刚刚"
} else if interval < 3600 {
return "\(Int(interval / 60))分钟前"
} else if interval < 86400 {
return "\(Int(interval / 3600))小时前"
} else if interval < 2592000 {
return "\(Int(interval / 86400))天前"
} else {
formatter.dateFormat = "MM-dd"
return formatter.string(from: date)
}
}
}
//
// YHCustomSearchView.swift
// galaxy
//
// Created by alexzzw on 2025/9/24.
// Copyright © 2025 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
protocol YHCustomSearchViewDelegate: AnyObject {
func searchView(_ searchView: YHCustomSearchView, didSearchWithText text: String?)
func searchViewDidBeginEditing(_ searchView: YHCustomSearchView)
func searchViewDidEndEditing(_ searchView: YHCustomSearchView)
}
class YHCustomSearchView: UIView {
weak var delegate: YHCustomSearchViewDelegate?
// MARK: - UI Components
private lazy var containerView: UIView = {
let view = UIView()
view.backgroundColor = .brandGrayColor1
return view
}()
private lazy var searchIconImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "resource_search")
imageView.contentMode = .scaleAspectFit
return imageView
}()
private lazy var searchTextField: UITextField = {
let textField = UITextField()
textField.font = UIFont.PFSC_M(ofSize: 14)
textField.textColor = UIColor.brandGrayColor8
textField.borderStyle = .none
textField.delegate = self
textField.addTarget(self, action: #selector(textFieldEditingChanged(_:)), for: .editingChanged)
textField.addTarget(self, action: #selector(textFieldEditingDidBegin(_:)), for: .editingDidBegin)
textField.addTarget(self, action: #selector(textFieldEditingDidEnd(_:)), for: .editingDidEnd)
textField.returnKeyType = .search
textField.attributedPlaceholder = NSAttributedString.init(string: "搜索企业名称、企业供需", attributes: [.foregroundColor: UIColor.brandGrayColor6, .font: UIFont.PFSC_R(ofSize: 14)])
return textField
}()
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupUI()
}
// MARK: - Setup
private func setupUI() {
backgroundColor = .white
addSubview(containerView)
containerView.addSubview(searchIconImageView)
containerView.addSubview(searchTextField)
setupConstraints()
}
private func setupConstraints() {
containerView.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(20)
make.centerY.equalToSuperview()
make.height.equalTo(36)
}
searchIconImageView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.centerY.equalToSuperview()
make.width.height.equalTo(24)
}
searchTextField.snp.makeConstraints { make in
make.left.equalTo(searchIconImageView.snp.right).offset(6)
make.right.equalToSuperview().offset(-12)
make.top.bottom.equalToSuperview()
}
}
// MARK: - Public Methods
var text: String? {
get {
return searchTextField.text
}
set {
searchTextField.text = newValue
}
}
var placeholder: String? {
get {
return searchTextField.placeholder
}
set {
searchTextField.placeholder = newValue
}
}
@discardableResult
override func becomeFirstResponder() -> Bool {
return searchTextField.becomeFirstResponder()
}
@discardableResult
override func resignFirstResponder() -> Bool {
return searchTextField.resignFirstResponder()
}
func clearText() {
searchTextField.text = ""
delegate?.searchView(self, didSearchWithText: "")
}
// MARK: - Actions
@objc private func textFieldEditingChanged(_ textField: UITextField) {
delegate?.searchView(self, didSearchWithText: textField.text)
}
@objc private func textFieldEditingDidBegin(_ textField: UITextField) {
delegate?.searchViewDidBeginEditing(self)
}
@objc private func textFieldEditingDidEnd(_ textField: UITextField) {
delegate?.searchViewDidEndEditing(self)
}
}
// MARK: - UITextFieldDelegate
extension YHCustomSearchView: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
delegate?.searchView(self, didSearchWithText: textField.text)
return true
}
}
//
// YHResourceCategoryView.swift
// galaxy
//
// Created by alexzzw on 2025/9/24.
// Copyright © 2025 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
protocol YHResourceCategoryViewDelegate: AnyObject {
func categoryView(_ view: YHResourceCategoryView, didSelectCategories categories: [YHResourceCategory], isSet: Bool)
}
class YHResourceCategoryView: UIView {
weak var delegate: YHResourceCategoryViewDelegate?
private var categories: [YHResourceCategory] = []
private var selectedCategories: [YHResourceCategory] = []
// MARK: - UI Components
private lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 8
layout.minimumInteritemSpacing = 8
layout.sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 100, right: 16) // 增加底部间距,为按钮留出空间
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = .white
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(YHResourceCategoryCell.self, forCellWithReuseIdentifier: "YHResourceCategoryCell")
collectionView.showsVerticalScrollIndicator = false
return collectionView
}()
// 底部按钮容器视图
private lazy var bottomButtonContainer: UIView = {
let view = UIView()
view.backgroundColor = .white
return view
}()
private lazy var resetButton: UIControl = {
let btn = UIControl()
let label = UILabel()
label.text = "重置"
label.textColor = .brandGrayColor8
label.font = UIFont.PFSC_M(ofSize: 12)
let imageView = UIImageView(image: UIImage(named: "resource_filter_reset"))
btn.addSubview(label)
btn.addSubview(imageView)
label.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.bottom.equalToSuperview()
}
imageView.snp.makeConstraints { make in
make.bottom.equalTo(label.snp.top)
make.centerX.equalTo(label)
make.width.height.equalTo(24)
}
btn.addTarget(self, action: #selector(resetButtonClicked), for: .touchUpInside)
return btn
}()
private lazy var confirmButton: UIButton = {
let btn = UIButton(type: .custom)
btn.setTitle("筛选", for: .normal)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.PFSC_R(ofSize: 15)
btn.backgroundColor = UIColor.brandGrayColor8
btn.layer.cornerRadius = 3
btn.addTarget(self, action: #selector(confirmButtonClicked), for: .touchUpInside)
return btn
}()
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
loadCategories()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupUI()
loadCategories()
}
// MARK: - Setup
private func setupUI() {
clipsToBounds = true
backgroundColor = .white
addSubview(collectionView)
addSubview(bottomButtonContainer)
bottomButtonContainer.addSubview(resetButton)
bottomButtonContainer.addSubview(confirmButton)
setupConstraints()
}
private func setupConstraints() {
collectionView.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.bottom.equalToSuperview() // collection view占满整个视图
}
bottomButtonContainer.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.height.equalTo(74) // 按钮容器高度
}
resetButton.snp.makeConstraints { make in
make.left.equalToSuperview()
make.top.equalToSuperview().offset(12)
make.width.equalTo(60)
make.height.equalTo(46)
}
confirmButton.snp.makeConstraints { make in
make.left.equalTo(resetButton.snp.right)
make.right.equalToSuperview().offset(-16)
make.top.equalToSuperview().offset(12)
make.height.equalTo(46)
}
}
private func loadCategories() {
// 模拟数据,实际使用时从接口获取
let allCategory = YHResourceCategory()
allCategory.id = "0"
allCategory.name = "全部行业"
let category1 = YHResourceCategory()
category1.id = "1"
category1.name = "金融会计"
let category2 = YHResourceCategory()
category2.id = "2"
category2.name = "资讯科技"
let category3 = YHResourceCategory()
category3.id = "3"
category3.name = "业务支持"
let category4 = YHResourceCategory()
category4.id = "4"
category4.name = "工业制造"
let category5 = YHResourceCategory()
category5.id = "5"
category5.name = "建筑工程"
let category6 = YHResourceCategory()
category6.id = "6"
category6.name = "地产开发"
let category7 = YHResourceCategory()
category7.id = "7"
category7.name = "法律服务"
let category8 = YHResourceCategory()
category8.id = "8"
category8.name = "商业贸易"
let category9 = YHResourceCategory()
category9.id = "9"
category9.name = "物流运输"
let category10 = YHResourceCategory()
category10.id = "10"
category10.name = "餐饮旅游"
let category11 = YHResourceCategory()
category11.id = "11"
category11.name = "广播娱乐"
let category12 = YHResourceCategory()
category12.id = "12"
category12.name = "艺术文化"
let category13 = YHResourceCategory()
category13.id = "13"
category13.name = "体育运动"
let category14 = YHResourceCategory()
category14.id = "14"
category14.name = "医疗健康"
let category15 = YHResourceCategory()
category15.id = "15"
category15.name = "学术教育"
let category16 = YHResourceCategory()
category16.id = "16"
category16.name = "其他"
categories = [
allCategory, category1, category2, category3, category4, category5,
category6, category7, category8, category9, category10, category11,
category12, category13, category14, category15, category16
]
collectionView.reloadData()
}
// MARK: - Public Methods
func setSelectedCategories(_ categories: [YHResourceCategory]) {
selectedCategories = categories
collectionView.reloadData()
}
// MARK: - Actions
@objc private func resetButtonClicked() {
// 重置为"全部行业"
selectedCategories.removeAll()
collectionView.reloadData()
delegate?.categoryView(self, didSelectCategories: selectedCategories, isSet: false)
}
@objc private func confirmButtonClicked() {
delegate?.categoryView(self, didSelectCategories: selectedCategories, isSet: true)
}
}
// MARK: - UICollectionViewDataSource, UICollectionViewDelegate
extension YHResourceCategoryView: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categories.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard categories.count > indexPath.item, let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YHResourceCategoryCell", for: indexPath) as? YHResourceCategoryCell else {
return UICollectionViewCell()
}
let category = categories[indexPath.item]
let isSelected = selectedCategories.contains { $0.id == category.id }
cell.configure(with: category, isSelected: isSelected)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let category = categories[indexPath.item]
// 如果是"全部行业",清空其他选择
if category.id == "0" {
selectedCategories.removeAll()
selectedCategories.append(category)
} else {
// 移除"全部行业"选项
selectedCategories.removeAll { $0.id == "0" }
// 切换选中状态
if let index = selectedCategories.firstIndex(where: { $0.id == category.id }) {
selectedCategories.remove(at: index)
} else {
selectedCategories.append(category)
}
// 如果没有选中任何分类,自动选中"全部行业"
if selectedCategories.isEmpty {
selectedCategories.append(categories.first!)
}
}
collectionView.reloadData()
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension YHResourceCategoryView: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// 计算每行三个cell的宽度
let sectionInset = 16.0 // 左右边距
let minimumInteritemSpacing = 8.0 // cell间距
let totalInset = sectionInset * 2 + minimumInteritemSpacing * 2 // 总的边距和间距
let availableWidth = collectionView.frame.width - totalInset
let cellWidth = availableWidth / 3.0 // 每行三个
return CGSize(width: cellWidth, height: 44) // 固定高度
}
}
// MARK: - YHResourceCategoryCell
class YHResourceCategoryCell: UICollectionViewCell {
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = UIFont.PFSC_R(ofSize: 14)
label.textAlignment = .center
label.textColor = .brandGrayColor8
label.numberOfLines = 1
label.adjustsFontSizeToFitWidth = true // 自动调整字体大小以适应宽度
label.minimumScaleFactor = 0.8
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupUI()
}
private func setupUI() {
backgroundColor = UIColor.brandGrayColor2
layer.cornerRadius = 2// 调整圆角以适应新高度
layer.borderWidth = 1
layer.borderColor = UIColor.clear.cgColor
contentView.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 12, left: 4, bottom: 12, right: 4))
}
}
func configure(with category: YHResourceCategory, isSelected: Bool) {
titleLabel.text = category.name
if isSelected {
layer.borderColor = UIColor.brandGrayColor8.cgColor
titleLabel.font = UIFont.PFSC_B(ofSize: 14)
} else {
layer.borderColor = UIColor.clear.cgColor
titleLabel.font = UIFont.PFSC_R(ofSize: 14)
}
}
}
//
// YHResourceTableViewCell.swift
// galaxy
//
// Created by alexzzw on 2025/9/24.
// Copyright © 2025 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import Kingfisher
class YHResourceTableViewCell: UITableViewCell {
static let labelFont = UIFont.PFSC_R(ofSize: 14)
static let labelHeight: CGFloat = 20.0
private let marginX: CGFloat = 20.0
private let logoWidth: CGFloat = 60.0
private let logoToRight: CGFloat = 10.0
private let marginBetweenVLine: CGFloat = 6.0
private let widthVLine: CGFloat = 1
private let arrowWidth: CGFloat = 24
private let arrowToLeft: CGFloat = 24
static let cellReuseIdentifier = "YHResourceTableViewCell"
// MARK: - 属性
var resourceModel: YHResourceListModel? {
didSet {
updateUI()
}
}
// MARK: - UI Components
// 分割线
lazy var separatorLine: UIView = {
let view = UIView()
view.backgroundColor = UIColor.brandGrayColor3
return view
}()
lazy var vSeparatorLine: UIView = {
let view = UIView()
view.backgroundColor = UIColor.brandGrayColor3
return view
}()
// 左侧LOGO
lazy var logoImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = 4
imageView.clipsToBounds = true
imageView.layer.borderColor = UIColor.brandGrayColor3.cgColor
imageView.layer.borderWidth = 1
imageView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.3)
return imageView
}()
lazy var typeTagIcon: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
return imageView
}()
lazy var rightArrow: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "resource_feed_arrow"))
imageView.contentMode = .scaleAspectFit
return imageView
}()
// 主标题
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = UIFont.PFSC_M(ofSize: 15)
label.textColor = UIColor.brandGrayColor8
label.numberOfLines = 2
return label
}()
// 公司名称
lazy var companyLabel: UILabel = {
let label = UILabel()
label.font = UIFont.PFSC_R(ofSize: 14)
label.textColor = UIColor.gray
label.numberOfLines = 1
return label
}()
// 行业标签
lazy var industryLabel: UILabel = {
let label = UILabel()
label.font = UIFont.PFSC_R(ofSize: 14)
label.textColor = UIColor.gray
label.numberOfLines = 1
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - 私有方法
private extension YHResourceTableViewCell {
func setupUI() {
backgroundColor = .white
selectionStyle = .none
contentView.addSubview(logoImageView)
contentView.addSubview(rightArrow)
contentView.addSubview(typeTagIcon)
contentView.addSubview(titleLabel)
contentView.addSubview(companyLabel)
contentView.addSubview(industryLabel)
contentView.addSubview(separatorLine)
contentView.addSubview(vSeparatorLine)
setupConstraints()
}
func setupConstraints() {
// LOGO约束
logoImageView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(marginX)
make.top.equalToSuperview().offset(24)
make.width.height.equalTo(logoWidth)
}
// 右上角标签约束
typeTagIcon.snp.makeConstraints { make in
make.left.equalTo(logoImageView.snp.right).offset(logoToRight)
make.top.equalTo(logoImageView)
make.height.equalTo(16)
make.width.equalTo(52)
}
// 主标题约束
titleLabel.snp.makeConstraints { make in
make.left.equalTo(logoImageView.snp.right).offset(logoToRight)
make.right.equalTo(rightArrow.snp.left)
make.top.equalTo(typeTagIcon.snp.bottom).offset(4)
}
rightArrow.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-marginX)
make.width.height.equalTo(arrowWidth)
make.top.equalTo(titleLabel)
}
// 公司名称约束
companyLabel.snp.makeConstraints { make in
make.left.equalTo(titleLabel)
make.top.equalTo(titleLabel.snp.bottom).offset(4)
}
vSeparatorLine.snp.makeConstraints { make in
make.left.equalTo(companyLabel.snp.right).offset(marginBetweenVLine)
make.centerY.equalTo(companyLabel)
make.width.equalTo(1)
make.height.equalTo(8)
}
// 行业标签约束
industryLabel.snp.makeConstraints { make in
make.left.equalTo(vSeparatorLine.snp.right).offset(marginBetweenVLine)
make.right.lessThanOrEqualTo(rightArrow.snp.left)
make.top.equalTo(companyLabel)
make.bottom.equalToSuperview().offset(-32)
}
// 分割线约束
separatorLine.snp.makeConstraints { make in
make.bottom.equalToSuperview()
make.left.right.equalToSuperview().inset(20)
make.height.equalTo(0.5)
}
}
private func getLabelWidth(_ text: String) -> CGFloat {
let attrString = NSAttributedString(string: text, attributes: [.font: YHResourceTableViewCell.labelFont])
let width = attrString.yh_width(containerHeight: YHResourceTableViewCell.labelHeight)
return width
}
func updateUI() {
guard let model = resourceModel else { return }
// 设置基本信息
let title = model.title.count > 0 ? model.title : "-"
let company = model.company_name.count > 0 ? model.company_name : "-"
let categoryName = model.category_name.count > 0 ? model.category_name : "-"
titleLabel.text = title
companyLabel.text = company
industryLabel.text = categoryName
let companyWidth = getLabelWidth(company)
let categoryNameWidth = getLabelWidth(categoryName)
if companyWidth + categoryNameWidth < KScreenWidth - logoWidth - logoToRight - arrowWidth - 2 * marginX - widthVLine - marginBetweenVLine * 2 {
companyLabel.snp.remakeConstraints { make in
make.left.equalTo(titleLabel)
make.top.equalTo(titleLabel.snp.bottom).offset(4)
}
vSeparatorLine.snp.remakeConstraints { make in
make.left.equalTo(companyLabel.snp.right).offset(marginBetweenVLine)
make.centerY.equalTo(companyLabel)
make.width.equalTo(1)
make.height.equalTo(8)
}
// 行业标签约束
industryLabel.snp.remakeConstraints { make in
make.left.equalTo(vSeparatorLine.snp.right).offset(marginBetweenVLine)
make.right.lessThanOrEqualTo(rightArrow.snp.left)
make.top.equalTo(companyLabel)
make.bottom.equalToSuperview().offset(-32)
}
} else {
companyLabel.snp.remakeConstraints { make in
make.left.equalTo(titleLabel)
make.right.lessThanOrEqualTo(rightArrow.snp.left)
make.top.equalTo(titleLabel.snp.bottom).offset(4)
}
vSeparatorLine.snp.remakeConstraints { make in
make.left.equalTo(companyLabel.snp.left)
make.centerY.equalTo(industryLabel)
make.width.equalTo(1)
make.height.equalTo(8)
}
// 行业标签约束
industryLabel.snp.remakeConstraints { make in
make.left.equalTo(vSeparatorLine.snp.right).offset(marginBetweenVLine)
make.right.lessThanOrEqualTo(rightArrow.snp.left)
make.top.equalTo(companyLabel.snp.bottom).offset(4)
make.bottom.equalToSuperview().offset(-32)
}
}
// 设置Logo
if !model.company_logo.isEmpty {
logoImageView.kf.setImage(
with: URL(string: model.company_logo),
placeholder: UIImage(named: "global_default_image")
)
} else {
logoImageView.image = UIImage(named: "global_default_image")
}
// 设置类型标签
if model.type == "service" {
typeTagIcon.image = UIImage(named: "resource_flag_service")
} else if model.type == "demand" {
typeTagIcon.image = UIImage(named: "resource_flag_demand")
} else {
typeTagIcon.image = nil
}
}
}
//
// YHResourceViewModel.swift
// galaxy
//
// Created by alexzzw on 2025/9/24.
// Copyright © 2025 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHResourceViewModel: NSObject {
// MARK: - 属性
var arrResourceData: [YHResourceListModel]?
var hasMoreForResource: Bool = true
var currentPage: Int = 1
var pageSize: Int = 20
var preloadItemIndex: Int = 10
// 筛选条件
var currentType: YHResourceFilterType = .service
var selectedCategories: [YHResourceCategory] = []
var searchKeyword: String?
override init() {
super.init()
}
// MARK: - 网络请求
/// 获取资源列表
/// - Parameters:
/// - firstFlag: 是否首次加载
/// - completion: 完成回调
func getResourceList(firstFlag: Bool, completion: @escaping (Bool, String?) -> Void) {
if firstFlag {
currentPage = 1
arrResourceData?.removeAll()
hasMoreForResource = true
} else {
if !hasMoreForResource {
completion(false, "没有更多数据")
return
}
}
var params: [String: Any] = [
"page": currentPage,
"page_size": pageSize,
"type": currentType == .service ? "service" : "demand"
]
// 添加分类筛选(多选)
if !selectedCategories.isEmpty {
let categoryIds = selectedCategories.map { $0.id }
params["category_ids"] = categoryIds
}
// 添加搜索关键词
if let keyword = searchKeyword, !keyword.isEmpty {
params["keyword"] = keyword
}
// 模拟网络延迟
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
let mockData = self.generateMockResourceData(page: self.currentPage)
if firstFlag {
self.arrResourceData = mockData
} else {
self.arrResourceData?.append(contentsOf: mockData)
}
// 计算每个模型的高度参数
self.arrResourceData?.forEach { $0.calHeightParam() }
self.currentPage += 1
self.hasMoreForResource = self.currentPage <= 5 // 模拟5页数据
completion(true, nil)
}
}
/// 切换收藏状态
/// - Parameters:
/// - resourceId: 资源ID
/// - completion: 完成回调
func toggleFavorite(resourceId: String, completion: @escaping (Bool, String?) -> Void) {
// 模拟网络延迟
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if let index = self.arrResourceData?.firstIndex(where: { $0.id == resourceId }) {
let item = self.arrResourceData![index]
item.updateFavoriteStatus(!item.is_favorite)
completion(true, nil)
} else {
completion(false, "资源不存在")
}
}
}
/// 获取资源分类
/// - Parameter completion: 完成回调
func getResourceCategories(completion: @escaping ([YHResourceCategory]?, String?) -> Void) {
// 模拟网络延迟
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let categories = self.generateMockCategories()
completion(categories, nil)
}
}
// MARK: - Mock数据生成
private func generateMockResourceData(page: Int) -> [YHResourceListModel] {
let serviceTitles = [
"专业财务咨询服务",
"企业IT系统开发",
"品牌营销策划方案",
"法律顾问服务",
"人力资源管理咨询",
"企业培训服务",
"网站建设与维护",
"财务审计服务"
]
let demandTitles = [
"寻找优质供应商",
"招聘高级技术人员",
"需要市场推广合作",
"寻求投资合作伙伴",
"需要办公场地租赁",
"寻找物流配送服务",
"需要设备采购咨询",
"寻求技术合作"
]
let companies = [
("北京科技有限公司哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈", "https://picsum.photos/60/60?random=1"),
("上海贸易集团", "https://picsum.photos/60/60?random=2"),
("深圳创新科技", "https://picsum.photos/60/60?random=3"),
("杭州电商公司", "https://picsum.photos/60/60?random=4"),
("成都软件开发", "https://picsum.photos/60/60?random=5"),
("广州制造企业", "https://picsum.photos/60/60?random=6"),
("武汉咨询公司", "https://picsum.photos/60/60?random=7"),
("西安技术公司", "https://picsum.photos/60/60?random=8")
]
let categories = [
("finance", "金融会计哈哈哈哈哈哈"),
("tech", "资讯科技"),
("business", "业务支援"),
("manufacturing", "工业制造"),
("legal", "法律服务"),
("trade", "商业贸易"),
("logistics", "物流运输"),
("other", "其他")
]
let contacts = [
("张经理", "138****1234", "zhang123"),
("李总监", "139****5678", "li_manager"),
("王助理", "136****9876", "wang_assistant"),
("赵主管", "137****4321", "zhao_supervisor"),
("孙经理", "135****8765", "sun_manager"),
("周总", "134****2468", "zhou_boss"),
("吴主任", "133****1357", "wu_director"),
("郑经理", "132****9753", "zheng_manager")
]
let locations = ["北京·朝阳区", "上海·浦东新区", "深圳·南山区", "杭州·西湖区", "成都·锦江区", "广州·天河区", "武汉·江汉区", "西安·雁塔区"]
let contents = [
"我们提供专业的财务咨询服务,帮助企业优化财务结构,降低运营成本,提高资金使用效率。团队拥有10年以上行业经验。",
"专业的IT系统开发团队,提供定制化解决方案,包括Web应用、移动应用、企业管理系统等。技术栈涵盖主流开发框架。",
"专注品牌营销策划,提供全方位的品牌推广方案。从品牌定位到执行落地,一站式服务,已服务500+企业客户。",
"资深律师团队提供专业法律顾问服务,涵盖合同审查、法律风险评估、争议解决等。保障企业合规经营。",
"人力资源管理专家,提供招聘、培训、绩效管理等全方位HR服务。帮助企业建立完善的人才管理体系。",
"企业培训专业机构,提供管理培训、技能培训、团队建设等服务。定制化课程设计,提升员工综合素质。",
"专业网站建设团队,提供网站设计、开发、运维一体化服务。响应式设计,SEO优化,提升企业网络形象。",
"注册会计师团队提供财务审计服务,确保财务报表真实可靠。严格按照审计准则执行,保障审计质量。"
]
// 根据筛选条件过滤数据
var filteredData: [YHResourceListModel] = []
for i in 0..<8 {
let model = YHResourceListModel()
let baseId = (page - 1) * 8 + i
model.id = "resource_\(baseId)"
// 根据当前类型选择标题
let titles = currentType == .service ? serviceTitles : demandTitles
model.title = titles[i % titles.count]
model.content = contents[i % contents.count]
model.type = currentType == .service ? "service" : "demand"
// 公司信息
let company = companies[i % companies.count]
model.company_name = company.0
model.company_logo = company.1
// 分类信息
let category = categories[i % categories.count]
model.category_id = category.0
model.category_name = category.1
// 联系信息
let contact = contacts[i % contacts.count]
model.contact_name = contact.0
model.contact_phone = contact.1
model.contact_wechat = contact.2
// 其他信息
model.location = locations[i % locations.count]
model.price = i % 3 == 0 ? "" : "\(Int.random(in: 1000...9999))"
model.price_unit = i % 3 == 0 ? "" : ["小时", "天", "月", "项目"][i % 4]
model.view_count = Int.random(in: 10...999)
model.favorite_count = Int.random(in: 0...50)
model.is_favorite = Bool.random()
model.status = 1 // 已发布
model.certification_status = i % 2 // 随机认证状态
// 时间
let daysAgo = Int.random(in: 0...30)
let date = Calendar.current.date(byAdding: .day, value: -daysAgo, to: Date()) ?? Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
model.created_time = formatter.string(from: date)
// 图片(随机添加1-3张图片)
let imageCount = Int.random(in: 0...3)
model.images = (0..<imageCount).map { "https://picsum.photos/300/200?random=\(baseId)_\($0)" }
// 标签
let allTags = ["优质服务", "专业团队", "价格优惠", "经验丰富", "快速响应", "全程跟踪", "售后保障", "行业领先"]
let tagCount = Int.random(in: 0...3)
model.tags = Array(allTags.shuffled().prefix(tagCount))
// 应用筛选条件
var shouldInclude = true
// 分类筛选
if !selectedCategories.isEmpty {
let categoryIds = selectedCategories.map { $0.id }
shouldInclude = shouldInclude && categoryIds.contains(model.category_id)
}
// 搜索关键词筛选
if let keyword = searchKeyword, !keyword.isEmpty {
let searchText = "\(model.title) \(model.content) \(model.company_name)".lowercased()
shouldInclude = shouldInclude && searchText.contains(keyword.lowercased())
}
if shouldInclude {
filteredData.append(model)
}
}
return filteredData
}
private func generateMockCategories() -> [YHResourceCategory] {
let categoryData = [
("finance", "金融会计", "💰"),
("tech", "资讯科技", "💻"),
("business", "业务支援", "📊"),
("manufacturing", "工业制造", "🏭"),
("construction", "建筑工程", "🏗️"),
("real_estate", "地产开发", "🏢"),
("legal", "法律服务", "⚖️"),
("trade", "商业贸易", "🛒"),
("logistics", "物流运输", "🚚"),
("food", "餐饮旅游", "🍽️"),
("media", "广播娱乐", "📺"),
("culture", "艺术文化", "🎨"),
("sports", "体育运动", "⚽"),
("health", "医疗健康", "🏥"),
("education", "学术教育", "📚"),
("other", "其他", "📋")
]
return categoryData.map { (id, name, icon) in
let category = YHResourceCategory()
category.id = id
category.name = name
category.icon = icon
category.resource_count = Int.random(in: 10...999)
category.is_hot = Int.random(in: 0...1) == 1
return category
}
}
}
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "resource_feed_arrow@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "resource_feed_arrow@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "resource_filter_down@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "resource_filter_down@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "resource_filter_reset@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "resource_filter_reset@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "resource_filter_up@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "resource_filter_up@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "resource_flag_demand@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "resource_flag_demand@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "resource_flag_service@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "resource_flag_service@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "resource_search@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "resource_search@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment