Commit 982b39ac authored by Alex朱枝文's avatar Alex朱枝文

社区调整

parent 1020b4e0
......@@ -10,11 +10,30 @@ import UIKit
import SnapKit
class YHCirclePublishViewController: YHBaseViewController {
struct UserInfo {
var userName: String = ""
var userTitle: String = ""
var avatar: String = ""
}
// MARK: - Properties
private var mediaItems: [YHSelectMediaItem] = []
private let maxMediaCount = 9
// private var resources: [YHResourceURLs] = []
private let marginX: CGFloat = 24
private let itemSpace: CGFloat = 8
var completion: (() -> Void)?
lazy var userInfoViewModel: YHInformationAuthorizeViewModel = YHInformationAuthorizeViewModel()
private var currentUser: UserInfo?
private var circleId: String?
lazy var viewModel: YHDynamicViewModel = YHDynamicViewModel()
// MARK: - Navigation Items
private lazy var leftBarItem: UIBarButtonItem = {
let item = UIBarButtonItem(image: UIImage(named: "nav_black_24")?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(cancelButtonTapped))
......@@ -29,9 +48,7 @@ class YHCirclePublishViewController: YHBaseViewController {
button.setTitle("发布", for: .normal)
button.titleLabel?.font = UIFont.PFSC_M(ofSize: 12)
button.setTitleColor(UIColor.brandGrayColor0, for: .normal)
button.setTitleColor(UIColor.brandGrayColor0, for: .disabled)
button.isEnabled = false
button.backgroundColor = UIColor.brandGrayColor4
button.backgroundColor = UIColor.brandGrayColor8
button.addTarget(self, action: #selector(publishButtonTapped), for: .touchUpInside)
return button
}()
......@@ -67,7 +84,6 @@ class YHCirclePublishViewController: YHBaseViewController {
private lazy var usernameLabel: UILabel = {
let label = UILabel()
label.text = "Monica杨晓丽"
label.font = UIFont.PFSC_R(ofSize: 14)
label.textColor = .brandGrayColor8
return label
......@@ -75,7 +91,6 @@ class YHCirclePublishViewController: YHBaseViewController {
private lazy var subtitleLabel: UILabel = {
let label = UILabel()
label.text = "董事长"
label.font = UIFont.PFSC_R(ofSize: 11)
label.textColor = UIColor.brandGrayColor6
return label
......@@ -145,9 +160,15 @@ class YHCirclePublishViewController: YHBaseViewController {
return view
}()
// MARK: - Properties
private var mediaItems: [YHSelectMediaItem] = []
private let maxMediaCount = 9
init(currentUser: UserInfo? = nil, id: String? = nil) {
self.currentUser = currentUser
self.circleId = id
super.init(nibName: nil, bundle: nil)
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
......@@ -155,6 +176,7 @@ class YHCirclePublishViewController: YHBaseViewController {
setupUI()
setupConstraints()
setupNotifications()
updateUserInfo()
}
deinit {
......@@ -261,6 +283,32 @@ class YHCirclePublishViewController: YHBaseViewController {
updateCollectionViewHeight()
}
private func updateUserInfo() {
if let currentUser = currentUser {
usernameLabel.text = currentUser.userName
subtitleLabel.text = currentUser.userTitle
if !currentUser.avatar.isEmpty, let url = URL(string: currentUser.avatar) {
avatarImageView.kf.setImage(with: url, placeholder: UIImage(named: "people_head_default"))
} else {
avatarImageView.image = UIImage(named: "people_head_default")
}
} else {
userInfoViewModel.requestUserInfo { [weak self] _, _ in
guard let self = self else {
return
}
self.usernameLabel.text = self.userInfoViewModel.model.username
self.subtitleLabel.text = self.userInfoViewModel.model.position
let avatar = self.userInfoViewModel.model.avatar
if !avatar.isEmpty, let url = URL(string: avatar) {
avatarImageView.kf.setImage(with: url, placeholder: UIImage(named: "people_head_default"))
} else {
avatarImageView.image = UIImage(named: "people_head_default")
}
}
}
}
private func setupNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
......@@ -281,13 +329,57 @@ class YHCirclePublishViewController: YHBaseViewController {
}
@objc private func publishButtonTapped() {
// 发布逻辑
showPublishingAlert()
guard hasContent() else {
YHHUD.flash(message: "请至少添加一张图片")
return
}
// 检查是否所有媒体都上传成功
let uploadingItems = mediaItems.filter { $0.uploadState == .uploading }
let failedItems = mediaItems.filter { $0.uploadState == .failed }
if !uploadingItems.isEmpty {
YHHUD.flash(message: "图片上传中,请稍候...")
return
}
if !failedItems.isEmpty {
YHHUD.flash(message: "部分图片上传失败,请重试")
return
}
// 收集所有成功上传的资源
let resources = mediaItems.compactMap { $0.resourceURL }
guard resources.count == mediaItems.count else {
YHHUD.flash(message: "图片上传未完成")
return
}
// 发布逻辑
YHGrayCommonAlertView.show("", "确认发布本次动态?", "返回", "确认发布", fullGuestureEnable: false) {
} callBack: { [weak self] in
guard let self = self else {
return
}
// 模拟发布延迟
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
// 调用发布接口,传入 resources
self.publishDynamic(with: resources)
}
}
// 7. 实际发布方法
private func publishDynamic(with resources: [YHResourceURLs]) {
let title = textView.text ?? ""
let content = detailTextView.text ?? ""
// 调用你的发布接口
viewModel.publishCircle(title, content, resources: resources) { [weak self] success, error in
guard let self = self else { return }
if success {
self.completion?()
self.dismiss(animated: true)
} else {
YHHUD.flash(message: error?.errorMsg ?? "发布失败")
}
}
}
......@@ -315,7 +407,8 @@ class YHCirclePublishViewController: YHBaseViewController {
// MARK: - Helper Methods
private func hasContent() -> Bool {
return !textView.text.isEmpty || !detailTextView.text.isEmpty || !mediaItems.isEmpty
return !mediaItems.isEmpty // 目前卡图片
// return !textView.text.isEmpty || !detailTextView.text.isEmpty || !mediaItems.isEmpty
}
private func updatePublishButton() {
......@@ -335,22 +428,15 @@ class YHCirclePublishViewController: YHBaseViewController {
}
private func showCancelAlert() {
let alert = UIAlertController(title: "确定要离开吗?", message: "离开后内容将不会保存", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
alert.addAction(UIAlertAction(title: "确定", style: .destructive) { _ in
self.dismiss(animated: true)
})
present(alert, animated: true)
YHGrayCommonAlertView.show("确定要离开吗?", "离开后内容将不会保存", "取消", "确定", fullGuestureEnable: false) {
} callBack: { [weak self] in
guard let self = self else {
return
}
private func showPublishingAlert() {
let alert = UIAlertController(title: "发布中...", message: nil, preferredStyle: .alert)
present(alert, animated: true)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
alert.dismiss(animated: true)
self.dismiss(animated: true)
}
}
}
// MARK: - UITextViewDelegate
......@@ -361,7 +447,7 @@ extension YHCirclePublishViewController: UITextViewDelegate {
} else if textView == detailTextView {
detailPlaceholderLabel.isHidden = !textView.text.isEmpty
}
updatePublishButton()
// updatePublishButton()
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
......@@ -425,7 +511,7 @@ extension YHCirclePublishViewController: UICollectionViewDataSource, UICollectio
// 刷新集合视图
self.mediaCollectionView.reloadData()
self.updateCollectionViewHeight()
self.updatePublishButton()
// self.updatePublishButton()
printLog("已从发布页面删除媒体项,剩余: \(self.mediaItems.count)")
}
......@@ -434,10 +520,17 @@ extension YHCirclePublishViewController: UICollectionViewDataSource, UICollectio
}
private func removeMedia(at index: Int) {
let item = mediaItems[index]
// 如果已经上传成功,可以选择是否通知后端删除
if item.uploadState == .success {
// TODO: 可选 - 通知后端删除资源
// viewModel.deleteResource(item.resourceURL?.url)
}
mediaItems.remove(at: index)
mediaCollectionView.reloadData()
updateCollectionViewHeight()
updatePublishButton()
// updatePublishButton()
}
private func showMediaUploadSheet() {
......@@ -457,9 +550,52 @@ extension YHCirclePublishViewController: UICollectionViewDataSource, UICollectio
mediaCollectionView.reloadData()
updateCollectionViewHeight()
updatePublishButton()
// updatePublishButton()
printLog("获得 \(itemsToAdd.count) 个媒体文件")
// 为每个新添加的 item 开始上传
itemsToAdd.forEach { item in
self.uploadMediaItem(item)
}
}
}
// 4. 独立的上传方法
private func uploadMediaItem(_ item: YHSelectMediaItem) {
guard let image = item.image else {
item.uploadState = .failed
return
}
item.uploadState = .uploading
viewModel.uploadImage(image, true) { [weak self, weak item] success, error in
guard let self = self, let item = item else { return }
if let error = error {
item.uploadState = .failed
item.uploadError = error
printLog("上传失败: \(item.name), error: \(error)")
return
}
let imageUrl = (success ?? "").getCdnUrl()
guard !imageUrl.isEmpty else {
item.uploadState = .failed
return
}
// 创建并保存到 item 自身
let source = YHResourceURLs()
source.url = imageUrl
source.width = "\(image.size.width)"
source.height = "\(image.size.height)"
source.type = 1
source.name = item.name
print("获得 \(itemsToAdd.count) 个媒体文件")
item.resourceURL = source
item.uploadState = .success
printLog("上传成功: \(item.name)")
}
}
}
......@@ -12,10 +12,8 @@ import JXSegmentedView
class YHCircleViewController: YHBaseViewController {
lazy var viewModel: YHCircleViewModel = {
let viewModel = YHCircleViewModel()
return viewModel
}()
lazy var viewModel: YHDynamicViewModel = YHDynamicViewModel()
var arr: [YHDynamicItem] = []
lazy var circleCollectView: UICollectionView = {
let flowLayout = YHHomeCollectionLayout()
......@@ -25,8 +23,7 @@ class YHCircleViewController: YHBaseViewController {
flowLayout.headerHeight = 112
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
collectionView.backgroundColor = .white
// collectionView.register(YHCircleCollectionViewCell.self, forCellWithReuseIdentifier: YHCircleCollectionViewCell.cellReuseIdentifier)
collectionView.register(YHHomeCollectionViewCell.self, forCellWithReuseIdentifier: YHHomeCollectionViewCell.cellReuseIdentifier)
collectionView.register(YHCircleCollectionViewCell.self, forCellWithReuseIdentifier: YHCircleCollectionViewCell.cellReuseIdentifier)
collectionView.register(YHCircleHeaderReusableView.self, forSupplementaryViewOfKind: CollectionViewWaterfallElementKindSectionHeader, withReuseIdentifier: YHCircleHeaderReusableView.reuseIdentifier)
collectionView.delegate = self
collectionView.dataSource = self
......@@ -49,18 +46,16 @@ class YHCircleViewController: YHBaseViewController {
return btn
}()
lazy var noDataView: YHEmptyDataView = {
let view = YHEmptyDataView.createView("暂无动态", kEmptyOrderBgName)
view.isHidden = true
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
addDefaultData()
getData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if viewModel.arrCircleData?.isEmpty == true {
getData()
}
requestList(true)
}
deinit {
......@@ -86,87 +81,49 @@ private extension YHCircleViewController {
make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-30)
}
view.addSubview(self.noDataView)
noDataView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(48)
make.width.equalTo(KScreenWidth)
make.height.equalTo(149)
}
publishButton.iconInLeft(spacing: 6)
circleCollectView.es.addYHPullToRefresh {
self.getData()
self.requestList(true)
}
circleCollectView.es.addInfiniteScrolling {
self.loadMoreData()
self.requestList(false)
}
circleCollectView.es.stopPullToRefresh()
circleCollectView.es.stopLoadingMore()
}
func addDefaultData() {
getCacheDataForCircleData()
}
func getCacheDataForCircleData() {
let arrString = UserDefaults.standard.value(forKey: "circleFirstPageData")
if let tmp = arrString as? String, let dic = tmp.toArray() {
guard let result = [YHCircleListModel].deserialize(from: dic) else {
func requestList(_ firstPage: Bool) {
let sourceType = 0
self.viewModel.getList(firstPage, sourceType: sourceType) { [weak self] _, _ in
guard let self = self else {
return
}
if let arrData = result as? [YHHomeListModel] {
for item in arrData {
item.calHeightParam()
}
self.viewModel.arrCircleData = arrData
self.circleCollectView.reloadData()
}
}
}
func getData() {
loadFirstData()
}
func loadFirstData() {
DispatchQueue.global().async {
self.viewModel.getCircleList(firstFlag: true) { [weak self] _, _ in
guard let self = self else { return }
if let arrData = self.viewModel.arrCircleData, arrData.count > 0 {
let arrString = arrData.toJSONString()
UserDefaults.standard.set(arrString, forKey: "circleFirstPageData")
UserDefaults.standard.synchronize()
self.arr.removeAll()
self.arr.append(contentsOf: (self.viewModel.dynamicArr))
for item in self.arr {
item.calHeightParam()
}
DispatchQueue.main.async {
self.circleCollectView.es.stopPullToRefresh()
if self.viewModel.hasMoreForCircle == false {
self.circleCollectView.es.noticeNoMoreData()
self.circleCollectView.footer?.alpha = 1
}
self.circleCollectView.reloadData()
}
}
}
}
func loadMoreData() {
DispatchQueue.global().async {
self.viewModel.getCircleList(firstFlag: false) { [weak self] _, _ in
guard let self = self else { return }
DispatchQueue.main.asyncAfter(wallDeadline: .now() + 0.35, execute: {
CATransaction.setDisableActions(true)
self.noDataView.isHidden = self.arr.count > 0
self.circleCollectView.reloadData()
CATransaction.commit()
self.circleCollectView.es.stopLoadingMore()
if self.viewModel.hasMoreForCircle == false {
self.circleCollectView.es.stopPullToRefresh()
if !self.viewModel.hasMore, self.arr.count > 0 {
self.circleCollectView.es.noticeNoMoreData()
self.circleCollectView.footer?.alpha = 1
}
})
}
}
}
}
......@@ -179,7 +136,7 @@ private extension YHCircleViewController {
let publishVC = YHCirclePublishViewController()
publishVC.completion = { [weak self] in
self?.getData()
self?.requestList(true)
}
let nav = YHNavigationController(rootViewController: publishVC)
nav.modalPresentationStyle = .fullScreen
......@@ -219,34 +176,56 @@ private extension YHCircleViewController {
private func handleOrganizeEventAction() {
NotificationCenter.default.post(name: YhConstant.YhNotification.goActivityVCNotifiction, object: nil)
}
private func gotoCircleDetailPage(id: String) {
var url = YHBaseUrlManager.shared.businessH5Url() + "/community/activity/\(id)"
if YHLoginManager.shared.isLogin() {
let token = YHLoginManager.shared.h5Token
let urlHasParam = String.hasQueryParameters(urlString: url)
if urlHasParam {
url = url + "&param=" + token
} else {
url = url + "?param=" + token
}
}
var tUrl = url
if !url.contains("navigationH=") {
tUrl = url + "?navigationH=\(k_Height_NavigationtBarAndStatuBar)"
if url.contains("?") {
tUrl = url + "&navigationH=\(k_Height_NavigationtBarAndStatuBar)"
}
}
let vc = YHH5WebViewVC()
vc.url = tUrl
navigationController?.pushViewController(vc)
}
}
// MARK: - UICollectionViewDelegate, UICollectionViewDataSource
extension YHCircleViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.viewModel.arrCircleData?.count ?? 0
return arr.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let datas = self.viewModel.arrCircleData, datas.count > indexPath.row else {
guard arr.count > indexPath.row else {
return UICollectionViewCell()
}
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: YHHomeCollectionViewCell.cellReuseIdentifier, for: indexPath) as? YHHomeCollectionViewCell else {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: YHCircleCollectionViewCell.cellReuseIdentifier, for: indexPath) as? YHCircleCollectionViewCell else {
return UICollectionViewCell()
}
cell.listModel = datas[indexPath.row]
// cell.clickHeartEvent = { [weak self] item in
// self?.viewModel.toggleLike(postId: item.id) { success, _ in
// if success {
// DispatchQueue.main.async {
// self?.circleCollectView.reloadItems(at: [indexPath])
// }
// }
// }
// }
cell.listModel = arr[indexPath.row]
cell.onImageHeightChanged = { [weak self] in
guard let self = self else {
return
}
UIView.performWithoutAnimation {
self.circleCollectView.reloadData()
}
}
return cell
}
......@@ -264,9 +243,12 @@ extension YHCircleViewController: UICollectionViewDelegate, UICollectionViewData
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let model = self.viewModel.arrCircleData?[indexPath.row] else { return }
guard arr.count > indexPath.row else { return }
let model = arr[indexPath.row]
printLog("\(model.img_width_use) \(model.img_height_use) \(model.img_width_cell) \(model.img_height_cell)")
// https://test-businessh5.galaxy-immi.com/community/activity/1
gotoCircleDetailPage(id: model.id)
//
}
}
......@@ -274,10 +256,9 @@ extension YHCircleViewController: UICollectionViewDelegate, UICollectionViewData
extension YHCircleViewController: CollectionViewWaterfallLayoutDelegate {
func collectionView(_ collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let count = self.viewModel.arrCircleData?.count ?? 0
if indexPath.row < count {
guard let model = self.viewModel.arrCircleData?[indexPath.row] else { return CGSize.zero }
if indexPath.row < arr.count {
let model = arr[indexPath.row]
return CGSize(width: model.img_width_cell, height: model.img_height_cell)
}
......@@ -300,7 +281,7 @@ extension YHCircleViewController {
let indexPathToCheck = IndexPath(item: itemIndex, section: section)
if circleCollectView.indexPathsForVisibleItems.contains(indexPathToCheck) {
loadMoreData()
requestList(false)
}
}
}
......
......@@ -7,175 +7,187 @@
//
import UIKit
import Kingfisher
// MARK: - Cell实现
class YHCircleCollectionViewCell: UICollectionViewCell {
static let cellReuseIdentifier = "YHCircleCollectionViewCell"
var likeBlock: ((YHCircleListModel) -> Void)?
var listModel: YHCircleListModel? {
var listModel: YHDynamicItem? {
didSet {
updateUI()
}
}
private lazy var containerView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.cornerRadius = 8
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 2)
view.layer.shadowRadius = 4
view.layer.shadowOpacity = 0.1
return view
var onImageHeightChanged: (() -> Void)?
// MARK: - UI Components
lazy var titleImageView: UIImageView = {
let imageV = UIImageView(image: UIImage(named: "plan_product_default"))
imageV.contentMode = .scaleAspectFill
imageV.isUserInteractionEnabled = true
imageV.clipsToBounds = true
return imageV
}()
private lazy var avatarImageView: UIImageView = {
let iv = UIImageView()
iv.layer.cornerRadius = 15
iv.layer.cornerRadius = 8
iv.clipsToBounds = true
iv.contentMode = .scaleAspectFill
iv.backgroundColor = UIColor(hex: 0xF5F5F5)
return iv
}()
private lazy var nameLabel: UILabel = {
let label = UILabel()
label.font = UIFont.PFSC_M(ofSize: 13)
label.textColor = UIColor.mainTextColor
label.font = UIFont.PFSC_R(ofSize: 12)
label.textColor = UIColor.brandGrayColor6
return label
}()
private lazy var timeLabel: UILabel = {
lazy var contentLabel: UILabel = {
let label = UILabel()
label.font = UIFont.PFSC_R(ofSize: 11)
label.textColor = UIColor.yhGreyColor
return label
}()
private lazy var contentLabel: UILabel = {
let label = UILabel()
label.font = UIFont.PFSC_R(ofSize: 14)
label.text = ""
label.textColor = UIColor.mainTextColor
label.numberOfLines = 0
label.font = UIFont.PFSC_R(ofSize: 15)
label.numberOfLines = 2
label.lineBreakMode = .byTruncatingTail
return label
}()
private lazy var imageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.layer.cornerRadius = 6
iv.backgroundColor = UIColor(hex: 0xF5F5F5)
return iv
lazy var subHoldView: UIView = {
let view = UIView()
view.layer.backgroundColor = UIColor.white.cgColor
view.layer.borderWidth = 0.6
view.layer.borderColor = UIColor(red: 0.943, green: 0.952, blue: 0.969, alpha: 1).cgColor
return view
}()
private lazy var likeButton: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "home_like_normal"), for: .normal)
btn.setImage(UIImage(named: "home_like_selected"), for: .selected)
btn.addTarget(self, action: #selector(likeButtonClicked), for: .touchUpInside)
return btn
}()
// MARK: - Initialization
private lazy var likeCountLabel: UILabel = {
let label = UILabel()
label.font = UIFont.PFSC_R(ofSize: 11)
label.textColor = UIColor.yhGreyColor
return label
}()
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Setup UI
private func setupUI() {
contentView.addSubview(containerView)
contentView.clipsToBounds = true
contentView.addSubview(titleImageView)
titleImageView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
make.height.equalTo(210)
}
containerView.addSubview(avatarImageView)
containerView.addSubview(nameLabel)
containerView.addSubview(timeLabel)
containerView.addSubview(contentLabel)
containerView.addSubview(imageView)
containerView.addSubview(likeButton)
containerView.addSubview(likeCountLabel)
contentView.addSubview(subHoldView)
subHoldView.snp.makeConstraints { make in
make.top.equalTo(titleImageView.snp.bottom)
make.bottom.left.right.equalToSuperview()
}
containerView.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(4)
// 内容文本
subHoldView.addSubview(contentLabel)
contentLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(12)
make.left.equalToSuperview().offset(8)
make.right.equalToSuperview().offset(-8)
}
// 头像
subHoldView.addSubview(avatarImageView)
avatarImageView.snp.makeConstraints { make in
make.left.top.equalToSuperview().offset(10)
make.width.height.equalTo(30)
make.left.equalToSuperview().offset(8)
make.top.equalTo(contentLabel.snp.bottom).offset(12)
make.width.height.equalTo(16)
}
// 昵称
subHoldView.addSubview(nameLabel)
nameLabel.snp.makeConstraints { make in
make.left.equalTo(avatarImageView.snp.right).offset(8)
make.top.equalTo(avatarImageView)
make.right.equalToSuperview().offset(-10)
make.left.equalTo(avatarImageView.snp.right).offset(4)
make.centerY.equalTo(avatarImageView)
make.right.equalToSuperview().offset(-8)
}
timeLabel.snp.makeConstraints { make in
make.left.equalTo(nameLabel)
make.bottom.equalTo(avatarImageView)
}
contentLabel.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(10)
make.top.equalTo(avatarImageView.snp.bottom).offset(8)
}
// MARK: - Update UI
imageView.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(10)
make.top.equalTo(contentLabel.snp.bottom).offset(8)
make.height.equalTo(100)
}
private func updateUI() {
guard let model = listModel else { return }
likeButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(10)
make.bottom.equalToSuperview().offset(-10)
make.width.height.equalTo(16)
// 顶部图片
titleImageView.snp.remakeConstraints { make in
make.left.right.top.equalToSuperview()
make.height.equalTo(model.img_height_use)
}
likeCountLabel.snp.makeConstraints { make in
make.left.equalTo(likeButton.snp.right).offset(4)
make.centerY.equalTo(likeButton)
if let firstResource = model.firstImageResource, !firstResource.url.isEmpty, let url = URL(string: firstResource.url) {
titleImageView.kf.setImage(with: url, placeholder: UIImage(named: "plan_product_default")) { result in
switch result {
case let .success(value):
DispatchQueue.global().async {
var needRefreshHeight = false
let size = value.image.size
let scale = size.width > 0 ? Double(size.height / size.width) : 0.0
if scale > 0 {
let width = (KScreenWidth - 47) / 2
let height = scale * width
if height >= model.maxImageHeight, model.img_height_use >= model.maxImageHeight {
return
}
if height <= model.minImageHeight, model.img_height_use <= model.minImageHeight {
return
}
if abs(height - model.img_height_use) > 10 {
model.calHeightParam(height)
needRefreshHeight = true
}
}
private func updateUI() {
guard let model = listModel else { return }
DispatchQueue.main.async {
if needRefreshHeight {
self.titleImageView.snp.remakeConstraints { make in
make.left.right.top.equalToSuperview()
make.height.equalTo(model.img_height_use)
}
self.onImageHeightChanged?()
}
}
}
nameLabel.text = model.user_name
timeLabel.text = model.create_time
contentLabel.text = model.content
case .failure:
printLog("kf setImage failed")
}
}
} else {
titleImageView.image = UIImage(named: "plan_product_default")
}
// 设置头像
// avatarImageView.yh_setImage(with: model.user_avatar)
// 用户信息
nameLabel.text = model.nickname.isEmpty ? "-" : model.nickname
// 设置图片
if !model.images.isEmpty {
imageView.isHidden = false
// imageView.yh_setImage(with: model.images.first)
// 头像
if !model.avatar.isEmpty, let url = URL(string: model.avatar) {
avatarImageView.kf.setImage(with: url, placeholder: UIImage(named: "people_head_default"))
} else {
imageView.isHidden = true
avatarImageView.image = UIImage(named: "people_head_default")
}
// 设置点赞状态
likeButton.isSelected = model.is_like
likeCountLabel.text = model.like_count > 0 ? "\(model.like_count)" : ""
likeCountLabel.textColor = model.is_like ? UIColor.brandMainColor : UIColor.yhGreyColor
// 内容文本
contentLabel.text = model.content
contentLabel.snp.removeConstraints()
contentLabel.snp.remakeConstraints { make in
make.top.equalToSuperview().offset(12)
make.left.equalToSuperview().offset(8)
make.right.equalToSuperview().offset(-8)
make.height.equalTo(model.textH)
}
@objc private func likeButtonClicked() {
guard let model = listModel else { return }
likeBlock?(model)
}
}
......@@ -482,13 +482,12 @@ private extension YHResourceViewController {
return
}
YHGrayCommonAlertView.show("需联系银河管家进行发布信息", "联系银河管家发布企业服务与企业需求,快来与我们联系吧~", "返回", "前往联系", fullGuestureEnable: false) { [weak self] in
YHGrayCommonAlertView.show("需联系银河管家进行发布信息", "联系银河管家发布企业服务与企业需求,快来与我们联系吧~", "返回", "前往联系", fullGuestureEnable: false) {
} callBack: { [weak self] in
guard let self = self else {
return
}
self.messageHandler.gotoChatVC(senderID: "")
} callBack: {
//
}
}
......
......@@ -154,13 +154,14 @@ class YHResourceDetailDocumentsCell: UITableViewCell {
return
}
let newImageHeight = image.size.height / image.size.width * imageWidth
if abs(newImageHeight - imageHeight) > 10 {
// 关键:用 updateConstraints 而不是 remakeConstraints
imageView.snp.updateConstraints { make in
make.height.equalTo(newImageHeight)
}
self.onImageHeightChanged?()
}
}
} else {
// 设置图片视图的高度
imageView.snp.makeConstraints { make in
......
......@@ -6,8 +6,8 @@
// Copyright © 2025 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import SmartCodable
import UIKit
class YHDynamicListModel: SmartCodable {
var total: Int = 0
......@@ -16,7 +16,6 @@ class YHDynamicListModel: SmartCodable {
var list: [YHDynamicItem] = []
required init() {
}
}
......@@ -24,14 +23,14 @@ class YHResourceURLs: SmartCodable {
var name: String = ""
var url: String = ""
var type: Int = 0
var width: String = "0"
var height: String = "0"
required init() {
}
}
class YHDynamicItem: SmartCodable {
var id: String = "" // 动态id
var user_id: String = ""
var title: String = ""
......@@ -56,7 +55,89 @@ class YHDynamicItem: SmartCodable {
var created_at: String = ""
var is_self_posts: Int = 0 // 是否是自己的动态
// MARK: - 高度计算相关属性
// 实际使用的图片宽高
var img_width_use: CGFloat = 0
var img_height_use: CGFloat = 0
// cell的宽高
var img_width_cell: CGFloat = 0
var img_height_cell: CGFloat = 0
// 描述的高度
var textH: CGFloat = 16.0
var isCalHeight = false
var maxImageHeight: CGFloat = 280
var minImageHeight: CGFloat = 140
var firstImageResource: YHResourceURLs? {
return resource_urls.first { resource in
resource.type == 1 && !resource.url.isEmpty
}
}
required init() {
}
func calHeightParam(_ defaultImageHeight: CGFloat = 0) {
if isCalHeight, defaultImageHeight <= 0 {
return
}
isCalHeight = true
let text = content
let width = (KScreenWidth - 47) / 2
let font = UIFont.PFSC_R(ofSize: 15) // 字体大小改为15
let maxWidth = width - 16 // 最大宽度限制(左右各8的内边距)
// 创建NSAttributedString对象并设置属性
let attributes = [NSAttributedString.Key.font: font] as [NSAttributedString.Key: Any]
// 根据指定的最大宽度和字体大小计算文本的高度
let size = (text as NSString).boundingRect(
with: CGSize(width: maxWidth, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: attributes,
context: nil
).size
var textHeight: CGFloat = size.height
// 固定2行的最大高度(行高约21,2行约42)
if textHeight > 42.0 {
textHeight = 42.0
} else if textHeight < 21 {
textHeight = 21
}
textH = textHeight
// 计算图片高度
var imageHeight: CGFloat = 0.0
if let firstResource = firstImageResource {
let imageSize = CGSize(width: Double(firstResource.width) ?? 0, height: Double(firstResource.height) ?? 0)
if imageSize.width == 0 || imageSize.height == 0, defaultImageHeight <= 0 {
imageHeight = width
} else {
imageHeight = defaultImageHeight > 0 ? defaultImageHeight : width * imageSize.height / imageSize.width
if imageHeight > maxImageHeight {
imageHeight = maxImageHeight
} else if imageHeight < minImageHeight {
imageHeight = minImageHeight
}
}
} else {
imageHeight = width
}
// 计算总高度
// 图片高度 + 顶部间距10 + 头像16 + 内容间距10 + 文本高度(约40) + 底部间距10
let totalHeight = imageHeight + 12 + 16 + 12 + textHeight + 12
img_width_use = width
img_height_use = imageHeight
img_width_cell = width
img_height_cell = totalHeight
}
}
......@@ -15,7 +15,7 @@ class YHNameCardDynamicCell: UITableViewCell {
static let imgH: CGFloat = (KScreenWidth-20*2-4*2)/3.0
var rePublishClick:(()->())?
var rePublishClick: (() -> Void)?
private lazy var publishLabel: UILabel = {
let label = UILabel()
......
......@@ -15,8 +15,9 @@ class YHDynamicViewModel: YHBaseViewModel {
var totalCount: Int = 0
var dynamicArr: [YHDynamicItem] = []
var hasMore: Bool = true
var preloadItemIndex: Int = 25
func getList(_ firstFlag: Bool, sourceType: Int, other_user_id: String, callBackBlock: @escaping (_ success: Bool, _ error: YHErrorModel?) -> Void) {
func getList(_ firstFlag: Bool, sourceType: Int, other_user_id: String = "", callBackBlock: @escaping (_ success: Bool, _ error: YHErrorModel?) -> Void) {
var params: [String: Any] = ["page": curPageIndex]
if firstFlag {
curPageIndex = 1
......@@ -73,3 +74,63 @@ class YHDynamicViewModel: YHBaseViewModel {
}
}
}
// MARK: - 点赞功能扩展
extension YHDynamicViewModel {
/// 点赞/取消点赞动态
/// - Parameters:
/// - id: 动态ID
/// - operation: 操作类型 1-点赞 2-取消点赞
/// - callBackBlock: 回调
func getLike(_ id: String, _ operation: Int, callBackBlock: @escaping (_ success: Bool, _ error: YHErrorModel?) -> Void) {
let params: [String: Any] = ["id": id, "operation": operation]
let strUrl = YHBaseUrlManager.shared.curURL() + YHAllApiName.Dynamic.like
_ = YHNetRequest.postRequest(url: strUrl, params: params) { json, _ in
if json.code == 200 {
callBackBlock(true, nil)
} else {
let error: YHErrorModel = YHErrorModel(errorCode: Int32(json.code), errorMsg: json.msg)
callBackBlock(false, error)
}
} failBlock: { err in
callBackBlock(false, err)
}
}
}
// MARK: - 发布动态
extension YHDynamicViewModel {
/// 发布动态
/// - Parameters:
/// - title: 标题
/// - content: 内容
/// - resources:图片/视频数组
/// - callBackBlock: 回调
func publishCircle(_ title: String, _ content: String, resources: [YHResourceURLs], callBackBlock: @escaping (_ success: Bool, _ error: YHErrorModel?) -> Void) {
var params: [String: Any] = ["title": title, "content": content]
params["resource_urls"] = resources.map { resource in
[
"name": resource.name,
"url": resource.url,
"type": resource.type,
"width": resource.width,
"height": resource.height
] as [String: Any]
}
let strUrl = YHBaseUrlManager.shared.curURL() + YHAllApiName.Dynamic.publishCircle
_ = YHNetRequest.postRequest(url: strUrl, params: params) { json, _ in
if json.code == 200 {
callBackBlock(true, nil)
} else {
let error: YHErrorModel = YHErrorModel(errorCode: Int32(json.code), errorMsg: json.msg)
callBackBlock(false, error)
}
} failBlock: { err in
callBackBlock(false, err)
}
}
}
......@@ -15,6 +15,18 @@ class YHSelectMediaItem {
var videoURL: URL?
var duration: TimeInterval?
// 新增:上传状态管理
var uploadState: UploadState = .pending
var resourceURL: YHResourceURLs? // 上传成功后的资源信息
var uploadError: YHErrorModel?
enum UploadState {
case pending // 待上传
case uploading // 上传中
case success // 上传成功
case failed // 上传失败
}
init(name: String = "", type: YHMediaType = .image, image: UIImage? = nil, videoURL: URL? = nil, duration: TimeInterval? = nil) {
self.name = name
self.type = type
......
......@@ -786,5 +786,7 @@ class YHAllApiName {
struct Dynamic {
static let list = "super-app/content/posts/list"
static let like = "super-app/content/posts/message-like"
static let publishCircle = "super-app/content/posts/add"
}
}
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