LNOrderListItemCell.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. //
  2. // LNOrderListItemCell.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/11/18.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. class LNOrderListItemCell: UITableViewCell {
  11. private let dateLabel = UILabel()
  12. private let stateLabel = UILabel()
  13. private let avatar = UIImageView()
  14. private let userNameLabel = UILabel()
  15. private let gameLabel = UILabel()
  16. private let priceLabel = UILabel()
  17. private let countLabel = UILabel()
  18. private let operationButton = UIButton()
  19. private let descLabel = UILabel()
  20. private let discountView = UIView()
  21. private let totalLabel = UILabel()
  22. private var curItem: LNOrderListItemVO?
  23. override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
  24. super.init(style: style, reuseIdentifier: reuseIdentifier)
  25. setupViews()
  26. }
  27. func update(_ item: LNOrderListItemVO) {
  28. avatar.showAvatar(item.avatar)
  29. dateLabel.text = TimeInterval(item.createTime / 1_000).formattedFullDate()
  30. userNameLabel.text = item.nickname
  31. gameLabel.text = item.bizCategoryName
  32. priceLabel.text = "\(item.price.toDisplay)"
  33. countLabel.text = "\(item.unit) x \(item.purchaseQty)"
  34. totalLabel.text = item.totalAmount.toDisplay
  35. discountView.isHidden = item.totalAmount >= item.price * Double(item.purchaseQty)
  36. curItem = item
  37. updateByStateChanged()
  38. }
  39. required init?(coder: NSCoder) {
  40. fatalError("init(coder:) has not been implemented")
  41. }
  42. }
  43. extension LNOrderListItemCell {
  44. @objc
  45. private func handleOperationButtonClick() {
  46. guard let orderItem = curItem else { return }
  47. switch orderItem.status {
  48. case .created, .waitingForAccept: // 可以取消
  49. LNCommonAlertView.showCancelOrderAlert(orderId: orderItem.orderId) { success in
  50. guard success else { return }
  51. orderItem.status = .cancelled
  52. }
  53. break
  54. case .completed: // 可以评价
  55. let panel = LNOrderCommentPanel()
  56. panel.update(orderItem.avatar, orderId: orderItem.orderId)
  57. panel.handler = { star, comment in
  58. orderItem.star = star
  59. }
  60. panel.popup()
  61. break
  62. case .serviceDone: // 可以点击完成
  63. LNCommonAlertView.showFinishOrderAlert(orderId: orderItem.orderId) { success in
  64. guard success else { return }
  65. orderItem.status = .completed
  66. }
  67. break
  68. case .refunded, .accepted, .rejected, .cancelled, .servicing:
  69. // 无操作选项
  70. break
  71. }
  72. }
  73. }
  74. extension LNOrderListItemCell {
  75. private func updateByStateChanged() {
  76. guard let curItem else { return }
  77. if curItem.refundApply {
  78. stateLabel.text = .init(key: "A00049")
  79. stateLabel.textColor = .init(hex: "#FFB836")
  80. operationButton.isHidden = true
  81. descLabel.isHidden = false
  82. descLabel.text = .init(key: "A00148")
  83. return
  84. }
  85. operationButton.isHidden = true
  86. operationButton.setBackgroundImage(nil, for: .normal)
  87. descLabel.isHidden = true
  88. switch curItem.status {
  89. case .created, .waitingForAccept:
  90. stateLabel.text = .init(key: "A00055")
  91. stateLabel.textColor = .init(hex: "#FFB836")
  92. operationButton.isHidden = false
  93. operationButton.layer.borderColor = UIColor.fill_3.cgColor
  94. operationButton.setTitleColor(.text_3, for: .normal)
  95. operationButton.setTitle(.init(key: "A00003"), for: .normal)
  96. case .completed:
  97. stateLabel.text = .init(key: "A00137")
  98. stateLabel.textColor = .text_6
  99. if curItem.star == 0 {
  100. operationButton.isHidden = false
  101. operationButton.layer.borderColor = UIColor.text_6.cgColor
  102. operationButton.setTitleColor(.text_6, for: .normal)
  103. operationButton.setTitle(.init(key: "A00149"), for: .normal)
  104. }
  105. case .refunded:
  106. stateLabel.text = .init(key: "A00138")
  107. stateLabel.textColor = .text_3
  108. operationButton.isHidden = true
  109. descLabel.isHidden = false
  110. descLabel.text = .init(key: "A00150")
  111. case .accepted:
  112. stateLabel.text = .init(key: "A00139")
  113. stateLabel.textColor = .text_6
  114. case .rejected:
  115. stateLabel.text = .init(key: "A00140")
  116. stateLabel.textColor = .text_6
  117. case .servicing:
  118. stateLabel.text = .init(key: "A00141")
  119. stateLabel.textColor = .init(hex: "#FFB836")
  120. case .serviceDone:
  121. stateLabel.text = .init(key: "A00141")
  122. stateLabel.textColor = .init(hex: "#FFB836")
  123. operationButton.isHidden = false
  124. operationButton.layer.borderColor = UIColor.clear.cgColor
  125. operationButton.setTitleColor(.text_1, for: .normal)
  126. operationButton.setTitle(.init(key: "A00151"), for: .normal)
  127. operationButton.setBackgroundImage(.primary_8, for: .normal)
  128. case .cancelled:
  129. stateLabel.text = .init(key: "A00152")
  130. stateLabel.textColor = .text_3
  131. }
  132. }
  133. private func setupViews() {
  134. backgroundColor = .clear
  135. onTap { [weak self] in
  136. guard let self else { return }
  137. guard let curItem else { return }
  138. pushToOrderDetail(orderId: curItem.orderId)
  139. }
  140. let container = UIView()
  141. container.backgroundColor = .fill
  142. container.layer.cornerRadius = 12
  143. contentView.addSubview(container)
  144. container.snp.makeConstraints { make in
  145. make.horizontalEdges.equalToSuperview().inset(16)
  146. make.top.equalToSuperview().offset(6)
  147. make.bottom.equalToSuperview().offset(-6)
  148. }
  149. let top = buildTopView()
  150. container.addSubview(top)
  151. top.snp.makeConstraints { make in
  152. make.horizontalEdges.equalToSuperview()
  153. make.top.equalToSuperview().offset(5)
  154. }
  155. let line = UIView()
  156. line.backgroundColor = .fill_2
  157. container.addSubview(line)
  158. line.snp.makeConstraints { make in
  159. make.horizontalEdges.equalToSuperview().inset(11)
  160. make.top.equalTo(top.snp.bottom)
  161. make.height.equalTo(0.5)
  162. }
  163. let mid = buildMidView()
  164. container.addSubview(mid)
  165. mid.snp.makeConstraints { make in
  166. make.horizontalEdges.equalToSuperview()
  167. make.top.equalTo(top.snp.bottom).offset(3)
  168. }
  169. let bottom = buildBottomView()
  170. container.addSubview(bottom)
  171. bottom.snp.makeConstraints { make in
  172. make.horizontalEdges.equalToSuperview()
  173. make.top.equalTo(mid.snp.bottom).offset(3)
  174. make.bottom.equalToSuperview().offset(-5)
  175. }
  176. }
  177. private func buildTopView() -> UIView {
  178. let container = UIView()
  179. container.snp.makeConstraints { make in
  180. make.height.equalTo(30)
  181. }
  182. stateLabel.font = .heading_h5
  183. container.addSubview(stateLabel)
  184. stateLabel.snp.makeConstraints { make in
  185. make.centerY.equalToSuperview()
  186. make.trailing.equalToSuperview().offset(-12)
  187. make.width.height.equalTo(0).priority(.low)
  188. }
  189. dateLabel.font = .body_xs
  190. dateLabel.textColor = .text_3
  191. container.addSubview(dateLabel)
  192. dateLabel.snp.makeConstraints { make in
  193. make.leading.equalToSuperview().offset(12)
  194. make.centerY.equalToSuperview()
  195. make.trailing.lessThanOrEqualTo(stateLabel.snp.leading).offset(-5)
  196. }
  197. return container
  198. }
  199. private func buildMidView() -> UIView {
  200. let container = UIView()
  201. let price = buildPriceView()
  202. container.addSubview(price)
  203. price.snp.makeConstraints { make in
  204. make.centerY.equalToSuperview()
  205. make.trailing.equalToSuperview().offset(-12)
  206. }
  207. let infoView = buildUserInfoView()
  208. container.addSubview(infoView)
  209. infoView.snp.makeConstraints { make in
  210. make.leading.equalToSuperview().offset(12)
  211. make.verticalEdges.equalToSuperview().inset(4)
  212. make.trailing.lessThanOrEqualTo(price.snp.leading).offset(-12)
  213. }
  214. return container
  215. }
  216. private func buildBottomView() -> UIView {
  217. let container = UIView()
  218. container.snp.makeConstraints { make in
  219. make.height.equalTo(38)
  220. }
  221. totalLabel.font = .heading_h3
  222. totalLabel.textColor = .text_5
  223. container.addSubview(totalLabel)
  224. totalLabel.snp.makeConstraints { make in
  225. make.centerY.equalToSuperview()
  226. make.trailing.equalToSuperview().offset(-12)
  227. }
  228. let coin = UIImageView.coinImageView()
  229. container.addSubview(coin)
  230. coin.snp.makeConstraints { make in
  231. make.centerY.equalToSuperview()
  232. make.trailing.equalTo(totalLabel.snp.leading).offset(-2)
  233. make.width.height.equalTo(20)
  234. }
  235. let text = UILabel()
  236. text.font = .body_s
  237. text.textColor = .text_5
  238. text.text = .init(key: "A00124")
  239. container.addSubview(text)
  240. text.snp.makeConstraints { make in
  241. make.centerY.equalToSuperview()
  242. make.trailing.equalTo(coin.snp.leading)
  243. }
  244. let discount = buildDiscount()
  245. container.addSubview(discount)
  246. discount.snp.makeConstraints { make in
  247. make.centerY.equalToSuperview()
  248. make.trailing.equalTo(text.snp.leading).offset(-5)
  249. }
  250. operationButton.layer.cornerRadius = 15
  251. operationButton.layer.borderWidth = 1
  252. operationButton.clipsToBounds = true
  253. operationButton.titleLabel?.font = .heading_h5
  254. operationButton.addTarget(self, action: #selector(handleOperationButtonClick), for: .touchUpInside)
  255. container.addSubview(operationButton)
  256. operationButton.snp.makeConstraints { make in
  257. make.height.equalTo(30)
  258. make.centerY.equalToSuperview()
  259. make.leading.equalToSuperview().offset(12)
  260. make.width.equalTo(77)
  261. }
  262. descLabel.font = .body_xs
  263. descLabel.textColor = .text_3
  264. descLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
  265. descLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
  266. container.addSubview(descLabel)
  267. descLabel.snp.makeConstraints { make in
  268. make.leading.equalToSuperview().offset(12)
  269. make.centerY.equalToSuperview()
  270. make.trailing.lessThanOrEqualTo(discount.snp.leading).offset(-16)
  271. }
  272. return container
  273. }
  274. private func buildUserInfoView() -> UIView {
  275. let container = UIView()
  276. container.onTap { [weak self] in
  277. guard let self else { return }
  278. guard let curItem else { return }
  279. pushToProfile(uid: curItem.sellerUserNo)
  280. }
  281. avatar.layer.cornerRadius = 20
  282. avatar.clipsToBounds = true
  283. container.addSubview(avatar)
  284. avatar.snp.makeConstraints { make in
  285. make.leading.verticalEdges.equalToSuperview()
  286. make.width.height.equalTo(40)
  287. }
  288. let textInfo = UIView()
  289. container.addSubview(textInfo)
  290. textInfo.snp.makeConstraints { make in
  291. make.centerY.equalToSuperview()
  292. make.leading.equalTo(avatar.snp.trailing).offset(8)
  293. make.trailing.equalToSuperview()
  294. }
  295. userNameLabel.textColor = .text_5
  296. userNameLabel.font = .heading_h4
  297. textInfo.addSubview(userNameLabel)
  298. userNameLabel.snp.makeConstraints { make in
  299. make.horizontalEdges.equalToSuperview()
  300. make.top.equalToSuperview()
  301. }
  302. gameLabel.font = .body_m
  303. gameLabel.textColor = .text_5
  304. textInfo.addSubview(gameLabel)
  305. gameLabel.snp.makeConstraints { make in
  306. make.horizontalEdges.equalToSuperview()
  307. make.bottom.equalToSuperview()
  308. make.top.equalTo(userNameLabel.snp.bottom).offset(2)
  309. }
  310. return container
  311. }
  312. private func buildPriceView() -> UIView {
  313. let container = UIView()
  314. let coin = UIImageView.coinImageView()
  315. container.addSubview(coin)
  316. coin.snp.makeConstraints { make in
  317. make.top.equalToSuperview()
  318. make.leading.equalToSuperview()
  319. make.width.height.equalTo(16)
  320. }
  321. priceLabel.font = .body_s
  322. priceLabel.textColor = .text_5
  323. container.addSubview(priceLabel)
  324. priceLabel.snp.makeConstraints { make in
  325. make.trailing.equalToSuperview()
  326. make.leading.equalTo(coin.snp.trailing)
  327. make.centerY.equalTo(coin)
  328. }
  329. countLabel.font = .body_xs
  330. countLabel.textColor = .text_4
  331. countLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
  332. countLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
  333. container.addSubview(countLabel)
  334. countLabel.snp.makeConstraints { make in
  335. make.trailing.bottom.equalToSuperview()
  336. make.top.equalTo(coin.snp.bottom).offset(2)
  337. }
  338. return container
  339. }
  340. private func buildDiscount() -> UIView {
  341. discountView.layer.cornerRadius = 4
  342. discountView.backgroundColor = .init(hex: "#FF6F32")
  343. let titleLabel = UILabel()
  344. titleLabel.text = .init(key: "A00129")
  345. titleLabel.font = .body_xs
  346. titleLabel.textColor = .text_1
  347. discountView.addSubview(titleLabel)
  348. titleLabel.snp.makeConstraints { make in
  349. make.horizontalEdges.equalToSuperview().inset(6)
  350. make.verticalEdges.equalToSuperview().inset(3)
  351. }
  352. return discountView
  353. }
  354. }
  355. #if DEBUG
  356. import SwiftUI
  357. struct LNOrderListItemCellPreview: UIViewRepresentable {
  358. func makeUIView(context: Context) -> some UIView {
  359. let container = UIView()
  360. container.backgroundColor = .lightGray
  361. let view = LNOrderListItemCell()
  362. container.addSubview(view)
  363. view.snp.makeConstraints { make in
  364. make.horizontalEdges.equalToSuperview()
  365. make.centerY.equalToSuperview()
  366. make.height.equalTo(133)
  367. }
  368. return container
  369. }
  370. func updateUIView(_ uiView: UIViewType, context: Context) { }
  371. }
  372. #Preview(body: {
  373. LNOrderListItemCellPreview()
  374. })
  375. #endif // DEBUG