LNCreateOrderFromSkillListPanel.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. //
  2. // LNCreateOrderFromSkillListPanel.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/12/2.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. class LNCreateOrderFromSkillListPanel: LNPopupView {
  11. private let skillListView = UIStackView()
  12. private let costLabel = UILabel()
  13. private let minusButton = UIButton()
  14. private let countLabel = UILabel()
  15. private let addButton = UIButton()
  16. private var curCount = 1 {
  17. didSet {
  18. countLabel.text = "\(curCount)"
  19. if curCount <= 1 {
  20. minusButton.isEnabled = false
  21. } else {
  22. minusButton.isEnabled = true
  23. }
  24. updatePrice()
  25. }
  26. }
  27. private var skillItemViews: [LNProfileOrderSkillItemView] = []
  28. private var curSelected: LNGameMateSkillVO? {
  29. didSet {
  30. skillItemViews.forEach {
  31. $0.isSelected = $0.cur?.id == curSelected?.id
  32. }
  33. updatePrice()
  34. }
  35. }
  36. override init(frame: CGRect) {
  37. super.init(frame: frame)
  38. setupViews()
  39. }
  40. func update(_ skills: [LNGameMateSkillVO], selected: LNGameMateSkillVO?) {
  41. guard !skills.isEmpty else { return }
  42. skillListView.arrangedSubviews.forEach {
  43. skillListView.removeArrangedSubview($0)
  44. $0.removeFromSuperview()
  45. }
  46. skills.forEach { skill in
  47. let view = LNProfileOrderSkillItemView()
  48. view.update(skill)
  49. view.onTap { [weak self] in
  50. guard let self else { return }
  51. self.curSelected = skill
  52. }
  53. skillListView.addArrangedSubview(view)
  54. skillItemViews.append(view)
  55. }
  56. curSelected = selected ?? skills[0]
  57. curCount = 1
  58. }
  59. required init?(coder: NSCoder) {
  60. fatalError("init(coder:) has not been implemented")
  61. }
  62. }
  63. extension LNCreateOrderFromSkillListPanel {
  64. private func updatePrice() {
  65. guard let skill = curSelected else { return }
  66. let cost = skill.price * Double(curCount)
  67. let text: String = .init(key: "A00121", cost.toDisplay, curCount, skill.unit)
  68. let attrStr = NSMutableAttributedString(string: text)
  69. let range = (text as NSString).range(of: "\(cost.toDisplay)")
  70. attrStr.addAttribute(.font, value: UIFont.heading_h2, range: range)
  71. costLabel.attributedText = attrStr
  72. }
  73. private func setupViews() {
  74. skillListView.axis = .vertical
  75. skillListView.spacing = 8
  76. container.addSubview(skillListView)
  77. skillListView.snp.makeConstraints { make in
  78. make.horizontalEdges.equalToSuperview().inset(16)
  79. make.top.equalToSuperview().offset(18)
  80. }
  81. let priceView = buildPriceView()
  82. container.addSubview(priceView)
  83. priceView.snp.makeConstraints { make in
  84. make.horizontalEdges.equalToSuperview()
  85. make.top.equalTo(skillListView.snp.bottom).offset(4)
  86. }
  87. let orderView = buildOrderButton()
  88. container.addSubview(orderView)
  89. orderView.snp.makeConstraints { make in
  90. make.horizontalEdges.equalToSuperview()
  91. make.top.equalTo(priceView.snp.bottom).offset(17)
  92. make.bottom.equalTo(commonBottomInset)
  93. }
  94. }
  95. private func buildPriceView() -> UIView {
  96. let container = UIView()
  97. container.snp.makeConstraints { make in
  98. make.height.equalTo(54)
  99. }
  100. let label = UILabel()
  101. label.text = .init(key: "A00122")
  102. label.font = .heading_h3
  103. label.textColor = .text_5
  104. container.addSubview(label)
  105. label.snp.makeConstraints { make in
  106. make.centerY.equalToSuperview()
  107. make.leading.equalToSuperview().offset(20)
  108. }
  109. addButton.setTitle("+", for: .normal)
  110. addButton.setTitleColor(.text_4, for: .normal)
  111. addButton.setTitleColor(.text_2, for: .disabled)
  112. addButton.backgroundColor = .primary_1
  113. addButton.layer.cornerRadius = 12
  114. addButton.addAction(UIAction(handler: { [weak self] _ in
  115. guard let self else { return }
  116. self.curCount += 1
  117. }), for: .touchUpInside)
  118. container.addSubview(addButton)
  119. addButton.snp.makeConstraints { make in
  120. make.centerY.equalToSuperview()
  121. make.trailing.equalToSuperview().offset(-20)
  122. make.width.height.equalTo(24)
  123. }
  124. countLabel.font = .body_m
  125. countLabel.textColor = .text_5
  126. container.addSubview(countLabel)
  127. countLabel.snp.makeConstraints { make in
  128. make.centerY.equalToSuperview()
  129. make.trailing.equalTo(addButton.snp.leading).offset(-10)
  130. }
  131. minusButton.setTitle("-", for: .normal)
  132. minusButton.setTitleColor(.text_4, for: .normal)
  133. minusButton.setTitleColor(.text_2, for: .disabled)
  134. minusButton.backgroundColor = .primary_1
  135. minusButton.layer.cornerRadius = 12
  136. minusButton.addAction(UIAction(handler: { [weak self] _ in
  137. guard let self else { return }
  138. self.curCount -= 1
  139. }), for: .touchUpInside)
  140. container.addSubview(minusButton)
  141. minusButton.snp.makeConstraints { make in
  142. make.centerY.equalToSuperview()
  143. make.trailing.equalTo(countLabel.snp.leading).offset(-10)
  144. make.width.height.equalTo(24)
  145. }
  146. return container
  147. }
  148. private func buildOrderButton() -> UIView {
  149. let container = UIView()
  150. let buttonView = UIView()
  151. buttonView.backgroundColor = .init(hex: "#1789FF14")
  152. buttonView.layer.cornerRadius = 23.5
  153. container.addSubview(buttonView)
  154. buttonView.snp.makeConstraints { make in
  155. make.horizontalEdges.equalToSuperview().inset(16)
  156. make.verticalEdges.equalToSuperview()
  157. make.height.equalTo(47)
  158. }
  159. let orderButton = UIButton()
  160. orderButton.setBackgroundImage(.icSkillOrderLong, for: .normal)
  161. orderButton.setTitle(.init(key: "A00041"), for: .normal)
  162. orderButton.setTitleColor(.text_1, for: .normal)
  163. orderButton.titleLabel?.font = .heading_h3
  164. orderButton.addAction(UIAction(handler: { [weak self] _ in
  165. guard let self else { return }
  166. guard let curSelected else { return }
  167. LNOrderManager.shared.createOrder(
  168. skillId: curSelected.id, count: curCount, remark: "")
  169. { [weak self] orderNo in
  170. guard let self else { return }
  171. guard orderNo != nil else { return }
  172. dismiss()
  173. }
  174. }), for: .touchUpInside)
  175. buttonView.addSubview(orderButton)
  176. orderButton.snp.makeConstraints { make in
  177. make.trailing.equalToSuperview()
  178. make.verticalEdges.equalToSuperview()
  179. }
  180. let descView = UIView()
  181. buttonView.addSubview(descView)
  182. descView.snp.makeConstraints { make in
  183. make.leading.verticalEdges.equalToSuperview()
  184. make.trailing.equalTo(orderButton.snp.leading)
  185. }
  186. let priceView = UIView()
  187. descView.addSubview(priceView)
  188. priceView.snp.makeConstraints { make in
  189. make.center.equalToSuperview()
  190. }
  191. let coin = UIImageView.coinImageView()
  192. priceView.addSubview(coin)
  193. coin.snp.makeConstraints { make in
  194. make.leading.centerY.equalToSuperview()
  195. make.width.height.equalTo(20)
  196. }
  197. costLabel.font = .body_s
  198. costLabel.textColor = .text_4
  199. priceView.addSubview(costLabel)
  200. costLabel.snp.makeConstraints { make in
  201. make.verticalEdges.trailing.equalToSuperview()
  202. make.leading.equalTo(coin.snp.trailing).offset(4)
  203. }
  204. return container
  205. }
  206. }
  207. private class LNProfileOrderSkillItemView: UIView {
  208. private let icon = UIImageView()
  209. private let nameLabel = UILabel()
  210. private let priceLabel = UILabel()
  211. var cur: LNGameMateSkillVO?
  212. var isSelected: Bool = false {
  213. didSet {
  214. if isSelected {
  215. layer.borderColor = UIColor.init(hex: "#1789FF").cgColor
  216. backgroundColor = .fill_5
  217. } else {
  218. layer.borderColor = UIColor.fill_4.cgColor
  219. backgroundColor = .clear
  220. }
  221. }
  222. }
  223. func update(_ skill: LNGameMateSkillVO) {
  224. icon.sd_setImage(with: URL(string: skill.icon))
  225. nameLabel.text = skill.name
  226. let text: String = .init(key: "A00040", skill.price.toDisplay, skill.unit)
  227. let attrStr = NSMutableAttributedString(string: text)
  228. let range = (text as NSString).range(of: "\(skill.price.toDisplay)")
  229. attrStr.addAttribute(.font, value: UIFont.heading_h4, range: range)
  230. priceLabel.attributedText = attrStr
  231. cur = skill
  232. }
  233. override init(frame: CGRect) {
  234. super.init(frame: frame)
  235. layer.cornerRadius = 12
  236. layer.borderWidth = 1
  237. layer.borderColor = UIColor.fill_4.cgColor
  238. snp.makeConstraints { make in
  239. make.height.equalTo(52)
  240. }
  241. addSubview(icon)
  242. icon.snp.makeConstraints { make in
  243. make.leading.equalToSuperview().offset(14)
  244. make.centerY.equalToSuperview()
  245. make.width.height.equalTo(32)
  246. }
  247. priceLabel.font = .body_s
  248. priceLabel.textColor = .text_4
  249. priceLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
  250. priceLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
  251. addSubview(priceLabel)
  252. priceLabel.snp.makeConstraints { make in
  253. make.centerY.equalToSuperview()
  254. make.trailing.equalToSuperview().offset(-15)
  255. }
  256. nameLabel.font = .heading_h4
  257. nameLabel.textColor = .text_5
  258. addSubview(nameLabel)
  259. nameLabel.snp.makeConstraints { make in
  260. make.leading.equalTo(icon.snp.trailing).offset(10)
  261. make.centerY.equalToSuperview()
  262. }
  263. }
  264. required init?(coder: NSCoder) {
  265. fatalError("init(coder:) has not been implemented")
  266. }
  267. }
  268. #if DEBUG
  269. import SwiftUI
  270. struct LNProfileOrderPanelPreview: UIViewRepresentable {
  271. func makeUIView(context: Context) -> some UIView {
  272. let container = UIView()
  273. container.backgroundColor = .lightGray
  274. let view = LNCreateOrderFromSkillListPanel()
  275. view.popup(container)
  276. return container
  277. }
  278. func updateUIView(_ uiView: UIViewType, context: Context) { }
  279. }
  280. #Preview(body: {
  281. LNProfileOrderPanelPreview()
  282. })
  283. #endif // DEBUG