261 lines
9.3 KiB
Swift
261 lines
9.3 KiB
Swift
//
|
|
// SettingViewController.swift
|
|
// suixinkan
|
|
//
|
|
|
|
import SnapKit
|
|
import UIKit
|
|
|
|
/// 设置中心页面,对齐 Android `SettingScreen` 的当前可见设置项。
|
|
final class SettingViewController: BaseViewController {
|
|
private let viewModel: SettingViewModel
|
|
private let settingAPI: SettingAPI
|
|
|
|
private let contentView = UIView()
|
|
private let cardView = UIView()
|
|
private let rowsStack = UIStackView()
|
|
private let deregistrationRow = SettingMenuRow(title: "注销当前门店身份", titleColor: AppColor.danger, showsDivider: false)
|
|
private let versionRow = SettingMenuRow(title: "系统版本", showsChevron: false)
|
|
private let copyrightLabel = UILabel()
|
|
|
|
init(
|
|
viewModel: SettingViewModel = SettingViewModel(),
|
|
settingAPI: SettingAPI? = nil
|
|
) {
|
|
self.viewModel = viewModel
|
|
self.settingAPI = settingAPI ?? NetworkServices.shared.settingAPI
|
|
super.init(nibName: nil, bundle: nil)
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func setupNavigationBar() {
|
|
title = "设置"
|
|
}
|
|
|
|
override func setupUI() {
|
|
view.backgroundColor = AppColor.pageBackground
|
|
|
|
view.addSubview(contentView)
|
|
contentView.addSubview(cardView)
|
|
cardView.addSubview(rowsStack)
|
|
contentView.addSubview(copyrightLabel)
|
|
|
|
cardView.backgroundColor = .white
|
|
cardView.layer.cornerRadius = 12
|
|
cardView.clipsToBounds = true
|
|
|
|
rowsStack.axis = .vertical
|
|
|
|
copyrightLabel.text = "Copyright © 2025 All Rights Reserved\n苏ICP备2025157647号"
|
|
copyrightLabel.font = .systemFont(ofSize: 14)
|
|
copyrightLabel.textColor = UIColor(hex: 0xB6BECA)
|
|
copyrightLabel.textAlignment = .center
|
|
copyrightLabel.numberOfLines = 0
|
|
|
|
let rows: [SettingMenuRow] = [
|
|
SettingMenuRow(title: "关于我们"),
|
|
versionRow,
|
|
SettingMenuRow(title: "App下载", value: "复制链接", valueColor: AppColor.primary, showsChevron: false),
|
|
SettingMenuRow(title: "用户协议"),
|
|
SettingMenuRow(title: "隐私政策", showsDivider: false),
|
|
]
|
|
rows.forEach(rowsStack.addArrangedSubview)
|
|
|
|
rows[0].addTarget(self, action: #selector(aboutTapped), for: .touchUpInside)
|
|
rows[2].addTarget(self, action: #selector(copyDownloadTapped), for: .touchUpInside)
|
|
rows[3].addTarget(self, action: #selector(userAgreementTapped), for: .touchUpInside)
|
|
rows[4].addTarget(self, action: #selector(privacyTapped), for: .touchUpInside)
|
|
if AppStore.shared.session.accountType == .storeUser {
|
|
rowsStack.addArrangedSubview(deregistrationRow)
|
|
deregistrationRow.addTarget(self, action: #selector(deregistrationTapped), for: .touchUpInside)
|
|
}
|
|
}
|
|
|
|
override func setupConstraints() {
|
|
contentView.snp.makeConstraints { make in
|
|
make.edges.equalTo(view.safeAreaLayoutGuide).inset(16)
|
|
}
|
|
cardView.snp.makeConstraints { make in
|
|
make.top.leading.trailing.equalToSuperview()
|
|
}
|
|
rowsStack.snp.makeConstraints { make in
|
|
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16))
|
|
}
|
|
copyrightLabel.snp.makeConstraints { make in
|
|
make.leading.trailing.equalToSuperview()
|
|
make.bottom.equalToSuperview().offset(-16)
|
|
}
|
|
}
|
|
|
|
override func bindActions() {
|
|
viewModel.onStateChange = { [weak self] in
|
|
Task { @MainActor in self?.applyViewModel() }
|
|
}
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
applyViewModel()
|
|
Task { await viewModel.checkLatestVersion(api: settingAPI) }
|
|
}
|
|
|
|
private func applyViewModel() {
|
|
versionRow.configure(value: viewModel.appVersion, showsUpgrade: viewModel.hasNewVersion)
|
|
}
|
|
|
|
@objc private func aboutTapped() {
|
|
openAgreement(.aboutUs)
|
|
}
|
|
|
|
@objc private func copyDownloadTapped() {
|
|
UIPasteboard.general.string = viewModel.appDownloadURL.absoluteString
|
|
showToast("已复制到剪贴板!")
|
|
}
|
|
|
|
@objc private func userAgreementTapped() {
|
|
openAgreement(.userAgreement)
|
|
}
|
|
|
|
@objc private func privacyTapped() {
|
|
openAgreement(.privacyPolicy)
|
|
}
|
|
|
|
/// 仅从当前门店业务会话创建注销流程,冻结 Token 与门店用户 ID。
|
|
@objc private func deregistrationTapped() {
|
|
do {
|
|
let session = AppStore.shared.session
|
|
let identity = try StoreAccountDeregistrationIdentity(session: session)
|
|
let api = StoreAccountDeregistrationAPI(client: NetworkServices.shared.apiClient, identity: identity) {
|
|
identity.matches(session: session)
|
|
}
|
|
let controller = StoreAccountDeregistrationViewController(
|
|
identityName: identity.displayName,
|
|
viewModel: StoreAccountDeregistrationViewModel(
|
|
storeUserID: Int(identity.userID) ?? 0,
|
|
submissionStore: StoreAccountDeregistrationSubmissionStore(storeUserID: identity.userID)
|
|
), api: api, onUnresolvedSubmission: {
|
|
NotificationCenter.default.post(name: NotificationName.storeAccountDeregistrationRestricted,
|
|
object: nil, userInfo: [NotificationUserInfoKey.deregistrationRequestToken: identity.token])
|
|
}
|
|
)
|
|
controller.hidesBottomBarWhenPushed = true
|
|
navigationController?.pushViewController(controller, animated: true)
|
|
} catch { showError(error.localizedDescription) }
|
|
}
|
|
|
|
private func openAgreement(_ kind: SettingAgreementKind) {
|
|
let destination = viewModel.agreementDestination(for: kind)
|
|
navigationController?.pushViewController(
|
|
AgreementWebViewController(title: destination.title, url: destination.url),
|
|
animated: true
|
|
)
|
|
}
|
|
}
|
|
|
|
/// 设置中心菜单行,展示标题、右侧文本、更新标记和箭头。
|
|
final class SettingMenuRow: UIControl {
|
|
private let titleLabel = UILabel()
|
|
private let rightStack = UIStackView()
|
|
private let valueLabel = UILabel()
|
|
private let upgradeImageView = UIImageView()
|
|
private let chevronImageView = UIImageView()
|
|
private let divider = UIView()
|
|
private let showsChevron: Bool
|
|
|
|
/// 创建菜单行,可为注销等操作单独指定标题颜色,不影响其他行。
|
|
init(
|
|
title: String,
|
|
titleColor: UIColor = UIColor(hex: 0x4B5563),
|
|
value: String? = nil,
|
|
valueColor: UIColor = AppColor.textPrimary,
|
|
showsChevron: Bool = true,
|
|
showsDivider: Bool = true
|
|
) {
|
|
self.showsChevron = showsChevron
|
|
super.init(frame: .zero)
|
|
setupUI()
|
|
setupConstraints()
|
|
titleLabel.text = title
|
|
titleLabel.textColor = titleColor
|
|
valueLabel.text = value
|
|
valueLabel.textColor = valueColor
|
|
chevronImageView.isHidden = !showsChevron
|
|
divider.isHidden = !showsDivider
|
|
accessibilityTraits.insert(.button)
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override var isHighlighted: Bool {
|
|
didSet {
|
|
backgroundColor = isHighlighted ? UIColor.black.withAlphaComponent(0.04) : .clear
|
|
}
|
|
}
|
|
|
|
func configure(value: String?, showsUpgrade: Bool) {
|
|
valueLabel.text = value
|
|
upgradeImageView.isHidden = !showsUpgrade
|
|
}
|
|
|
|
private func setupUI() {
|
|
titleLabel.font = .systemFont(ofSize: 14)
|
|
|
|
valueLabel.font = .systemFont(ofSize: 14)
|
|
valueLabel.textColor = AppColor.textPrimary
|
|
|
|
upgradeImageView.image = UIImage(systemName: "arrow.up.circle")?.withRenderingMode(.alwaysTemplate)
|
|
upgradeImageView.tintColor = UIColor(hex: 0x1F1F1F)
|
|
upgradeImageView.contentMode = .scaleAspectFit
|
|
upgradeImageView.isHidden = true
|
|
|
|
chevronImageView.image = UIImage(systemName: "chevron.right")?.withRenderingMode(.alwaysTemplate)
|
|
chevronImageView.tintColor = AppColor.textTertiary
|
|
chevronImageView.contentMode = .scaleAspectFit
|
|
|
|
divider.backgroundColor = AppColor.border
|
|
|
|
rightStack.axis = .horizontal
|
|
rightStack.alignment = .center
|
|
rightStack.spacing = 4
|
|
[valueLabel, upgradeImageView, chevronImageView].forEach(rightStack.addArrangedSubview)
|
|
|
|
[titleLabel, rightStack, divider].forEach(addSubview)
|
|
}
|
|
|
|
private func setupConstraints() {
|
|
snp.makeConstraints { make in
|
|
make.height.equalTo(52)
|
|
}
|
|
|
|
titleLabel.snp.makeConstraints { make in
|
|
make.leading.centerY.equalToSuperview()
|
|
}
|
|
|
|
upgradeImageView.snp.makeConstraints { make in
|
|
make.width.height.equalTo(24)
|
|
}
|
|
|
|
chevronImageView.snp.makeConstraints { make in
|
|
make.width.height.equalTo(24)
|
|
}
|
|
|
|
rightStack.snp.makeConstraints { make in
|
|
make.centerY.equalToSuperview()
|
|
make.trailing.equalToSuperview()
|
|
make.leading.greaterThanOrEqualTo(titleLabel.snp.trailing).offset(12)
|
|
}
|
|
|
|
divider.snp.makeConstraints { make in
|
|
make.leading.trailing.bottom.equalToSuperview()
|
|
make.height.equalTo(0.5)
|
|
}
|
|
}
|
|
}
|