Introduce unified design tokens and migrate Home, Orders, and Statistics UI.

Establish AppColor/AppFont/AppSpacing/AppRadius theming, shared components, and design-system docs so pilot screens align with Android while keeping iOS-native spacing and touch targets.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 17:58:18 +08:00
parent 39186cfe25
commit 87696a4774
31 changed files with 737 additions and 213 deletions

View File

@ -13,7 +13,7 @@ class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
view.backgroundColor = AppColor.pageBackground
setupNavigationBar()
setupUI()
setupConstraints()
@ -61,14 +61,14 @@ class BaseViewController: UIViewController {
let container = UIView()
container.backgroundColor = UIColor.black.withAlphaComponent(0.8)
container.layer.cornerRadius = 8
container.layer.cornerRadius = AppRadius.sm
container.clipsToBounds = true
container.alpha = 0
let label = UILabel()
label.text = message
label.textColor = .white
label.font = .systemFont(ofSize: 14)
label.font = .app(.body)
label.textAlignment = .center
label.numberOfLines = 0

View File

@ -16,6 +16,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
) {
guard let windowScene = scene as? UIWindowScene else { return }
AppNavigationBarAppearance.applyGlobalAppearance()
let window = UIWindow(windowScene: windowScene)
window.rootViewController = AppRouter.makeRootViewController()
window.makeKeyAndVisible()

View File

@ -5,15 +5,56 @@
import UIKit
/// Android `theme/Color.kt`
/// Android `theme/Color.kt` `ScenicQueueTokens`
enum AppColor {
static let primary = UIColor(red: 0 / 255, green: 115 / 255, blue: 255 / 255, alpha: 1)
static let text333 = UIColor(red: 51 / 255, green: 51 / 255, blue: 51 / 255, alpha: 1)
static let text666 = UIColor(red: 102 / 255, green: 102 / 255, blue: 102 / 255, alpha: 1)
static let agreementLink = UIColor(red: 32 / 255, green: 139 / 255, blue: 255 / 255, alpha: 1)
static let inputBackground = UIColor(red: 244 / 255, green: 244 / 255, blue: 244 / 255, alpha: 1)
static let secondaryButtonBackground = UIColor(red: 239 / 255, green: 246 / 255, blue: 255 / 255, alpha: 1)
static let buttonDisabled = UIColor(red: 192 / 255, green: 192 / 255, blue: 192 / 255, alpha: 1)
// MARK: - Brand
static let primary = UIColor(hex: 0x0073FF)
static let primaryBanner = UIColor(hex: 0x1677FF)
static let primaryLight = UIColor(hex: 0xEFF6FF)
// MARK: - Background
static let pageBackground = UIColor(hex: 0xF5F5F5)
static let pageBackgroundSoft = UIColor(hex: 0xF7FAFF)
static let cardBackground = UIColor.white
static let inputBackground = UIColor(hex: 0xF4F4F4)
// MARK: - Border
static let border = UIColor(hex: 0xEEEEEE)
static let cardOutline = UIColor(hex: 0xE9ECEF)
// MARK: - Text
static let textPrimary = UIColor(hex: 0x333333)
static let textSecondary = UIColor(hex: 0x666666)
static let textTertiary = UIColor(hex: 0x999999)
static let textTabInactive = UIColor(hex: 0x7D8DA3)
// MARK: - Semantic
static let success = UIColor(hex: 0x10B981)
static let successBackground = UIColor(hex: 0xF0FDF4)
static let warning = UIColor(hex: 0xFF7B00)
static let warningBackground = UIColor(hex: 0xFFF0E2)
static let danger = UIColor(hex: 0xEF4444)
static let dangerBackground = UIColor(hex: 0xFFE7E7)
static let info = UIColor(hex: 0x0073FF)
static let infoBackground = UIColor(hex: 0xE8F4FF)
static let online = UIColor(hex: 0x22C55E)
// MARK: - Overlay
static let overlayScrim = UIColor.black.withAlphaComponent(0.2)
// MARK: - Legacy aliases
static let text333 = textPrimary
static let text666 = textSecondary
static let agreementLink = UIColor(hex: 0x208BFF)
static let secondaryButtonBackground = primaryLight
static let buttonDisabled = UIColor(hex: 0xC0C0C0)
static let placeholder = UIColor.gray
}

View File

@ -0,0 +1,54 @@
//
// AppFont.swift
// suixinkan
//
import UIKit
/// 8pt SF ProSystem
enum AppFont {
case caption
case captionMedium
case body
case bodyMedium
case subtitle
case title
case metric
case display
/// UIFont
var uiFont: UIFont {
switch self {
case .caption:
return .systemFont(ofSize: 12, weight: .regular)
case .captionMedium:
return .systemFont(ofSize: 12, weight: .medium)
case .body:
return .systemFont(ofSize: 14, weight: .regular)
case .bodyMedium:
return .systemFont(ofSize: 14, weight: .medium)
case .subtitle:
return .systemFont(ofSize: 16, weight: .medium)
case .title:
return .systemFont(ofSize: 16, weight: .bold)
case .metric:
return .systemFont(ofSize: 18, weight: .bold)
case .display:
return .systemFont(ofSize: 28, weight: .semibold)
}
}
/// UILabel
func apply(to label: UILabel) {
label.font = uiFont
}
}
extension UIFont {
/// `AppFont`
static func app(_ style: AppFont) -> UIFont {
style.uiFont
}
}

View File

@ -0,0 +1,28 @@
//
// AppNavigationBarAppearance.swift
// suixinkan
//
import UIKit
/// UINavigationBar
enum AppNavigationBarAppearance {
/// shadow
static func applyGlobalAppearance() {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = AppColor.cardBackground
appearance.shadowColor = .clear
appearance.titleTextAttributes = [
.foregroundColor: AppColor.textPrimary,
.font: UIFont.app(.title),
]
let navigationBar = UINavigationBar.appearance()
navigationBar.standardAppearance = appearance
navigationBar.scrollEdgeAppearance = appearance
navigationBar.compactAppearance = appearance
navigationBar.tintColor = AppColor.primary
}
}

View File

@ -0,0 +1,20 @@
//
// AppRadius.swift
// suixinkan
//
import CoreGraphics
/// Android ScenicQueueTokens
enum AppRadius {
static let xs: CGFloat = 4
static let sm: CGFloat = 8
static let md: CGFloat = 10
static let lg: CGFloat = 12
static let xl: CGFloat = 16
static let sheetTop: CGFloat = 14
/// pillStatistics
static let pill: CGFloat = 18
}

View File

@ -0,0 +1,18 @@
//
// AppShadow.swift
// suixinkan
//
import UIKit
///
enum AppShadow {
/// layer
static func applyCardShadow(to layer: CALayer) {
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.08
layer.shadowOffset = CGSize(width: 0, height: 4)
layer.shadowRadius = 12
}
}

View File

@ -0,0 +1,33 @@
//
// AppSpacing.swift
// suixinkan
//
import CoreGraphics
/// 8pt
enum AppSpacing {
static let xxs: CGFloat = 4
static let xs: CGFloat = 8
static let sm: CGFloat = 12
static let md: CGFloat = 16
static let lg: CGFloat = 24
static let xl: CGFloat = 32
static let xxl: CGFloat = 48
///
static let screenHorizontalInset: CGFloat = md
/// Apple HIG
static let minTouchTarget: CGFloat = 44
/// Header
static let homeHeaderHeight: CGFloat = 52
///
static let buttonHeight: CGFloat = 46
///
static let formRowHeight: CGFloat = 52
}

View File

@ -32,14 +32,14 @@ final class AppButton: UIButton {
}
private func setupUI(title: String) {
titleLabel?.font = .systemFont(ofSize: 14, weight: .regular)
layer.cornerRadius = 10
titleLabel?.font = .app(.body)
layer.cornerRadius = AppRadius.md
clipsToBounds = true
setTitle(title, for: .normal)
updateAppearance()
snp.makeConstraints { make in
make.height.equalTo(46)
make.height.equalTo(AppSpacing.buttonHeight)
}
}

View File

@ -0,0 +1,44 @@
//
// AppCardView.swift
// suixinkan
//
import SnapKit
import UIKit
///
final class AppCardView: UIView {
/// container
let contentView = UIView()
private let showsBorder: Bool
/// - Parameter showsBorder: 1px falseflat
init(showsBorder: Bool = false) {
self.showsBorder = showsBorder
super.init(frame: .zero)
setupUI()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
backgroundColor = AppColor.cardBackground
layer.cornerRadius = AppRadius.md
clipsToBounds = true
if showsBorder {
layer.borderWidth = 1
layer.borderColor = AppColor.cardOutline.cgColor
}
addSubview(contentView)
contentView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}

View File

@ -0,0 +1,82 @@
//
// AppStatusBadge.swift
// suixinkan
//
import SnapKit
import UIKit
/// / badge
final class AppStatusBadge: UIView {
///
enum Tone {
case info
case success
case warning
case danger
case neutral
}
private let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
AppFont.captionMedium.apply(to: label)
label.textAlignment = .center
addSubview(label)
label.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(
UIEdgeInsets(
top: AppSpacing.xxs,
left: AppSpacing.xs,
bottom: AppSpacing.xxs,
right: AppSpacing.xs
)
)
}
layer.cornerRadius = AppRadius.xs
clipsToBounds = true
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
///
func apply(tone: Tone, text: String) {
label.text = text
switch tone {
case .info:
label.textColor = AppColor.info
backgroundColor = AppColor.infoBackground
case .success:
label.textColor = AppColor.success
backgroundColor = AppColor.successBackground
case .warning:
label.textColor = AppColor.warning
backgroundColor = AppColor.warningBackground
case .danger:
label.textColor = AppColor.danger
backgroundColor = AppColor.dangerBackground
case .neutral:
label.textColor = AppColor.textTabInactive
backgroundColor = AppColor.inputBackground
}
}
/// Android OrderView
func applyOrderStatus(_ status: Int, text: String) {
switch status {
case 18, 20:
apply(tone: .info, text: text)
case 30:
apply(tone: .success, text: text)
case 50:
apply(tone: .danger, text: text)
default:
apply(tone: .warning, text: text)
}
}
}

View File

@ -30,10 +30,10 @@ final class HomeViewController: BaseViewController {
}
override func setupUI() {
view.backgroundColor = UIColor(hex: 0xF5F5F5)
view.backgroundColor = AppColor.pageBackground
contentStack.axis = .vertical
contentStack.spacing = 12
contentStack.spacing = AppSpacing.sm
scrollView.showsVerticalScrollIndicator = false
view.addSubview(scenicHeaderView)
@ -51,15 +51,15 @@ final class HomeViewController: BaseViewController {
scenicHeaderView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide)
make.leading.trailing.equalToSuperview()
make.height.equalTo(52)
make.height.equalTo(AppSpacing.homeHeaderHeight)
}
scrollView.snp.makeConstraints { make in
make.top.equalTo(scenicHeaderView.snp.bottom)
make.leading.trailing.bottom.equalToSuperview()
}
contentStack.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(16)
make.width.equalTo(scrollView.snp.width).offset(-32)
make.edges.equalToSuperview().inset(AppSpacing.screenHorizontalInset)
make.width.equalTo(scrollView.snp.width).offset(-AppSpacing.screenHorizontalInset * 2)
}
}

View File

@ -47,8 +47,8 @@ final class HomeCommonAppsGridView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
titleLabel.text = "常用应用"
titleLabel.font = .systemFont(ofSize: 16, weight: .bold)
titleLabel.textColor = AppColor.text333
titleLabel.font = .app(.title)
titleLabel.textColor = AppColor.textPrimary
collectionView.backgroundColor = .clear
collectionView.delegate = self
@ -61,7 +61,7 @@ final class HomeCommonAppsGridView: UIView {
make.top.leading.trailing.equalToSuperview()
}
collectionView.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(12)
make.top.equalTo(titleLabel.snp.bottom).offset(AppSpacing.sm)
make.leading.trailing.bottom.equalToSuperview()
make.height.equalTo(88)
}
@ -101,8 +101,8 @@ final class HomeMenuCell: UICollectionViewCell {
iconView.tintColor = AppColor.primary
iconView.contentMode = .scaleAspectFit
titleLabel.font = .systemFont(ofSize: 12)
titleLabel.textColor = AppColor.text333
titleLabel.font = .app(.caption)
titleLabel.textColor = AppColor.textPrimary
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 2
@ -110,13 +110,13 @@ final class HomeMenuCell: UICollectionViewCell {
contentView.addSubview(titleLabel)
iconView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(8)
make.top.equalToSuperview().offset(AppSpacing.xs)
make.centerX.equalToSuperview()
make.width.height.equalTo(28)
}
titleLabel.snp.makeConstraints { make in
make.top.equalTo(iconView.snp.bottom).offset(8)
make.leading.trailing.equalToSuperview().inset(4)
make.top.equalTo(iconView.snp.bottom).offset(AppSpacing.xs)
make.leading.trailing.equalToSuperview().inset(AppSpacing.xxs)
}
}

View File

@ -17,23 +17,23 @@ final class HomeLocationReportCardView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
layer.cornerRadius = 10
backgroundColor = AppColor.cardBackground
layer.cornerRadius = AppRadius.md
titleLabel.text = "位置上报"
titleLabel.font = .systemFont(ofSize: 16, weight: .semibold)
titleLabel.textColor = AppColor.text333
titleLabel.font = .app(.title)
titleLabel.textColor = AppColor.textPrimary
addressLabel.font = .systemFont(ofSize: 13)
addressLabel.textColor = AppColor.text666
addressLabel.font = .app(.body)
addressLabel.textColor = AppColor.textSecondary
addressLabel.numberOfLines = 2
reportButton.setTitle("立即上报", for: .normal)
reportButton.setTitleColor(.white, for: .normal)
reportButton.backgroundColor = AppColor.primary
reportButton.layer.cornerRadius = 6
reportButton.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
reportButton.contentEdgeInsets = UIEdgeInsets(top: 8, left: 14, bottom: 8, right: 14)
reportButton.layer.cornerRadius = AppRadius.sm
reportButton.titleLabel?.font = .app(.bodyMedium)
reportButton.contentEdgeInsets = UIEdgeInsets(top: AppSpacing.xs, left: 14, bottom: AppSpacing.xs, right: 14)
reportButton.addTarget(self, action: #selector(reportTapped), for: .touchUpInside)
addSubview(titleLabel)
@ -41,16 +41,16 @@ final class HomeLocationReportCardView: UIView {
addSubview(reportButton)
titleLabel.snp.makeConstraints { make in
make.top.leading.equalToSuperview().offset(15)
make.top.leading.equalToSuperview().offset(AppSpacing.screenHorizontalInset)
}
addressLabel.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(8)
make.leading.equalToSuperview().offset(15)
make.bottom.equalToSuperview().offset(-15)
make.trailing.lessThanOrEqualTo(reportButton.snp.leading).offset(-12)
make.top.equalTo(titleLabel.snp.bottom).offset(AppSpacing.xs)
make.leading.equalToSuperview().offset(AppSpacing.screenHorizontalInset)
make.bottom.equalToSuperview().offset(-AppSpacing.screenHorizontalInset)
make.trailing.lessThanOrEqualTo(reportButton.snp.leading).offset(-AppSpacing.sm)
}
reportButton.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-15)
make.trailing.equalToSuperview().offset(-AppSpacing.screenHorizontalInset)
make.centerY.equalToSuperview()
}
}

View File

@ -19,7 +19,7 @@ final class HomeQuickActionsView: UIView {
super.init(frame: frame)
stackView.axis = .horizontal
stackView.distribution = .fillEqually
stackView.spacing = 12
stackView.spacing = AppSpacing.sm
addSubview(stackView)
stackView.snp.makeConstraints { make in
make.edges.equalToSuperview()
@ -45,8 +45,8 @@ final class HomeQuickActionsView: UIView {
private func makeActionButton(title: String, icon: String, action: Selector) -> UIView {
let container = UIView()
container.backgroundColor = .white
container.layer.cornerRadius = 10
container.backgroundColor = AppColor.cardBackground
container.layer.cornerRadius = AppRadius.md
let button = UIButton(type: .system)
button.addTarget(self, action: action, for: .touchUpInside)
@ -61,8 +61,8 @@ final class HomeQuickActionsView: UIView {
let label = UILabel()
label.text = title
label.font = .systemFont(ofSize: 13, weight: .medium)
label.textColor = AppColor.text333
label.font = .app(.bodyMedium)
label.textColor = AppColor.textPrimary
label.textAlignment = .center
let column = UIStackView(arrangedSubviews: [imageView, label])

View File

@ -16,22 +16,22 @@ final class HomeScenicHeaderView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
titleLabel.font = .systemFont(ofSize: 16, weight: .medium)
titleLabel.textColor = AppColor.text333
arrowView.tintColor = AppColor.text666
backgroundColor = AppColor.cardBackground
titleLabel.font = .app(.subtitle)
titleLabel.textColor = AppColor.textPrimary
arrowView.tintColor = AppColor.textSecondary
arrowView.contentMode = .scaleAspectFit
addSubview(titleLabel)
addSubview(arrowView)
titleLabel.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(16)
make.leading.equalToSuperview().offset(AppSpacing.screenHorizontalInset)
make.centerY.equalToSuperview()
make.trailing.lessThanOrEqualTo(arrowView.snp.leading).offset(-8)
make.trailing.lessThanOrEqualTo(arrowView.snp.leading).offset(-AppSpacing.xs)
}
arrowView.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-16)
make.trailing.equalToSuperview().offset(-AppSpacing.screenHorizontalInset)
make.centerY.equalToSuperview()
make.width.height.equalTo(16)
}

View File

@ -15,17 +15,17 @@ final class HomeStoreCardView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
layer.cornerRadius = 10
backgroundColor = AppColor.cardBackground
layer.cornerRadius = AppRadius.md
nameLabel.font = .systemFont(ofSize: 16, weight: .semibold)
nameLabel.textColor = AppColor.text333
nameLabel.font = .app(.title)
nameLabel.textColor = AppColor.textPrimary
addressLabel.font = .systemFont(ofSize: 13)
addressLabel.textColor = AppColor.text666
addressLabel.font = .app(.body)
addressLabel.textColor = AppColor.textSecondary
addressLabel.numberOfLines = 2
statusLabel.font = .systemFont(ofSize: 12, weight: .medium)
statusLabel.font = .app(.captionMedium)
statusLabel.textColor = AppColor.primary
addSubview(nameLabel)
@ -33,17 +33,17 @@ final class HomeStoreCardView: UIView {
addSubview(statusLabel)
nameLabel.snp.makeConstraints { make in
make.top.leading.equalToSuperview().offset(15)
make.trailing.lessThanOrEqualTo(statusLabel.snp.leading).offset(-8)
make.top.leading.equalToSuperview().offset(AppSpacing.screenHorizontalInset)
make.trailing.lessThanOrEqualTo(statusLabel.snp.leading).offset(-AppSpacing.xs)
}
statusLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(15)
make.trailing.equalToSuperview().offset(-15)
make.top.equalToSuperview().offset(AppSpacing.screenHorizontalInset)
make.trailing.equalToSuperview().offset(-AppSpacing.screenHorizontalInset)
}
addressLabel.snp.makeConstraints { make in
make.top.equalTo(nameLabel.snp.bottom).offset(8)
make.leading.trailing.equalToSuperview().inset(15)
make.bottom.equalToSuperview().offset(-15)
make.top.equalTo(nameLabel.snp.bottom).offset(AppSpacing.xs)
make.leading.trailing.equalToSuperview().inset(AppSpacing.screenHorizontalInset)
make.bottom.equalToSuperview().offset(-AppSpacing.screenHorizontalInset)
}
}

View File

@ -18,18 +18,18 @@ final class HomeWorkStatusCardView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
layer.cornerRadius = 10
backgroundColor = AppColor.cardBackground
layer.cornerRadius = AppRadius.md
onlineButton.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium)
onlineButton.layer.cornerRadius = 4
onlineButton.titleLabel?.font = .app(.captionMedium)
onlineButton.layer.cornerRadius = AppRadius.xs
onlineButton.contentEdgeInsets = UIEdgeInsets(top: 6, left: 12, bottom: 6, right: 12)
onlineButton.addTarget(self, action: #selector(onlineTapped), for: .touchUpInside)
countdownLabel.font = .systemFont(ofSize: 16, weight: .medium)
countdownLabel.font = .app(.subtitle)
countdownLabel.textColor = AppColor.primary
reminderButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
reminderButton.titleLabel?.font = .app(.subtitle)
reminderButton.setTitleColor(AppColor.primary, for: .normal)
reminderButton.addTarget(self, action: #selector(reminderTapped), for: .touchUpInside)
@ -38,18 +38,18 @@ final class HomeWorkStatusCardView: UIView {
addSubview(reminderButton)
onlineButton.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(15)
make.leading.equalToSuperview().offset(AppSpacing.screenHorizontalInset)
make.centerY.equalToSuperview()
}
countdownLabel.snp.makeConstraints { make in
make.center.equalToSuperview()
}
reminderButton.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-15)
make.trailing.equalToSuperview().offset(-AppSpacing.screenHorizontalInset)
make.centerY.equalToSuperview()
}
snp.makeConstraints { make in
make.height.equalTo(52)
make.height.equalTo(AppSpacing.formRowHeight)
}
}
@ -61,11 +61,11 @@ final class HomeWorkStatusCardView: UIView {
func apply(isOnline: Bool, countdownText: String, reminderMinutes: Int) {
onlineButton.setTitle(isOnline ? "在线" : "离线", for: .normal)
if isOnline {
onlineButton.backgroundColor = UIColor(hex: 0x22C55E)
onlineButton.setTitleColor(UIColor(hex: 0xF0FDF4), for: .normal)
onlineButton.backgroundColor = AppColor.online
onlineButton.setTitleColor(AppColor.successBackground, for: .normal)
} else {
onlineButton.backgroundColor = UIColor(hex: 0xF4F4F4)
onlineButton.setTitleColor(UIColor(hex: 0x7B8EAA), for: .normal)
onlineButton.backgroundColor = AppColor.inputBackground
onlineButton.setTitleColor(AppColor.textTabInactive, for: .normal)
}
countdownLabel.text = countdownText
let reminderTitle = reminderMinutes == 0 ? "不提醒" : "提前\(reminderMinutes)分钟"

View File

@ -67,21 +67,21 @@ final class MainTabBarController: UITabBarController {
private func configureTabBarAppearance() {
tabBar.tintColor = AppColor.primary
tabBar.backgroundColor = .white
tabBar.backgroundColor = AppColor.cardBackground
tabBar.shadowImage = UIImage()
let normalAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor(hex: 0x7D8DA3),
.font: UIFont.systemFont(ofSize: 12, weight: .regular),
.foregroundColor: AppColor.textTabInactive,
.font: UIFont.app(.caption),
]
let selectedAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: AppColor.primary,
.font: UIFont.systemFont(ofSize: 12, weight: .medium),
.font: UIFont.app(.captionMedium),
]
let appearance = UITabBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .white
appearance.backgroundColor = AppColor.cardBackground
appearance.shadowColor = .clear
appearance.stackedLayoutAppearance.normal.titleTextAttributes = normalAttributes
appearance.stackedLayoutAppearance.selected.titleTextAttributes = selectedAttributes

View File

@ -107,34 +107,34 @@ final class OrderCardCell: UITableViewCell {
}
private func setupUI() {
cardView.backgroundColor = .white
cardView.layer.cornerRadius = 12
cardView.backgroundColor = AppColor.cardBackground
cardView.layer.cornerRadius = AppRadius.lg
orderNumberLabel.font = .systemFont(ofSize: 14)
orderNumberLabel.textColor = .black
createdAtLabel.font = .systemFont(ofSize: 12)
createdAtLabel.textColor = UIColor(hex: 0x6B7280)
typeChip.font = .systemFont(ofSize: 12)
orderNumberLabel.font = .app(.body)
orderNumberLabel.textColor = AppColor.textPrimary
createdAtLabel.font = .app(.caption)
createdAtLabel.textColor = AppColor.textSecondary
typeChip.font = .app(.caption)
typeChip.textColor = AppColor.primary
typeChip.backgroundColor = UIColor(hex: 0xEFF6FF)
typeChip.layer.cornerRadius = 4
typeChip.backgroundColor = AppColor.primaryLight
typeChip.layer.cornerRadius = AppRadius.xs
typeChip.clipsToBounds = true
typeChip.textAlignment = .center
configureActionButton(refundButton, title: "退款", color: UIColor(hex: 0xEF4444), bg: UIColor(hex: 0xFFE7E7))
configureActionButton(verifyButton, title: "核销订单", color: .white, bg: UIColor(hex: 0x0073FF))
configureActionButton(refundButton, title: "退款", color: AppColor.danger, bg: AppColor.dangerBackground)
configureActionButton(verifyButton, title: "核销订单", color: .white, bg: AppColor.primary)
refundButton.addTarget(self, action: #selector(refundTapped), for: .touchUpInside)
verifyButton.addTarget(self, action: #selector(verifyTapped), for: .touchUpInside)
uploadTaskButton.setTitle("上传任务", for: .normal)
uploadTaskButton.setTitleColor(AppColor.primary, for: .normal)
uploadTaskButton.titleLabel?.font = .systemFont(ofSize: 16)
uploadTaskButton.backgroundColor = UIColor(hex: 0xEFF6FF)
uploadTaskButton.layer.cornerRadius = 12
uploadTaskButton.titleLabel?.font = .app(.subtitle)
uploadTaskButton.backgroundColor = AppColor.primaryLight
uploadTaskButton.layer.cornerRadius = AppRadius.lg
uploadTaskButton.addTarget(self, action: #selector(uploadTaskTapped), for: .touchUpInside)
actionStack.axis = .horizontal
actionStack.spacing = 12
actionStack.spacing = AppSpacing.sm
actionStack.distribution = .fillEqually
actionStack.addArrangedSubview(refundButton)
actionStack.addArrangedSubview(verifyButton)
@ -143,27 +143,27 @@ final class OrderCardCell: UITableViewCell {
infoStack.spacing = 6
cancelButton.setTitle("取消订单", for: .normal)
cancelButton.setTitleColor(UIColor(hex: 0xEF4444), for: .normal)
cancelButton.titleLabel?.font = .systemFont(ofSize: 14)
cancelButton.setTitleColor(AppColor.danger, for: .normal)
cancelButton.titleLabel?.font = .app(.body)
cancelButton.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside)
orderSourceButton.setTitleColor(AppColor.primary, for: .normal)
orderSourceButton.titleLabel?.font = .systemFont(ofSize: 14)
orderSourceButton.titleLabel?.font = .app(.body)
orderSourceButton.contentHorizontalAlignment = .leading
orderSourceButton.addTarget(self, action: #selector(orderSourceTapped), for: .touchUpInside)
historicalButton.setTitle("历史拍摄", for: .normal)
historicalButton.setTitleColor(AppColor.primary, for: .normal)
historicalButton.titleLabel?.font = .systemFont(ofSize: 14)
historicalButton.titleLabel?.font = .app(.body)
historicalButton.addTarget(self, action: #selector(historicalTapped), for: .touchUpInside)
refinedStack.axis = .horizontal
refinedStack.spacing = 8
refinedStack.spacing = AppSpacing.xs
refinedNeedButton.setTitle("需要精修", for: .normal)
refinedNoButton.setTitle("无需精修", for: .normal)
[refinedNeedButton, refinedNoButton].forEach {
$0.titleLabel?.font = .systemFont(ofSize: 13)
$0.layer.cornerRadius = 6
$0.titleLabel?.font = .app(.body)
$0.layer.cornerRadius = AppRadius.sm
$0.layer.borderWidth = 1
}
refinedNeedButton.addTarget(self, action: #selector(refinedNeedTapped), for: .touchUpInside)
@ -185,7 +185,7 @@ final class OrderCardCell: UITableViewCell {
cardView.addSubview(cancelButton)
cardView.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 16, bottom: 12, right: 16))
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: AppSpacing.md, bottom: AppSpacing.sm, right: AppSpacing.md))
}
orderNumberLabel.snp.makeConstraints { make in
make.top.leading.equalToSuperview().inset(16)
@ -240,9 +240,9 @@ final class OrderCardCell: UITableViewCell {
private func configureActionButton(_ button: UIButton, title: String, color: UIColor, bg: UIColor) {
button.setTitle(title, for: .normal)
button.setTitleColor(color, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 16)
button.titleLabel?.font = .app(.subtitle)
button.backgroundColor = bg
button.layer.cornerRadius = 8
button.layer.cornerRadius = AppRadius.sm
}
private func addInfoRow(title: String, value: String) {
@ -251,12 +251,12 @@ final class OrderCardCell: UITableViewCell {
row.distribution = .fillEqually
let titleLabel = UILabel()
titleLabel.text = title
titleLabel.font = .systemFont(ofSize: 13)
titleLabel.textColor = UIColor(hex: 0x6B7280)
titleLabel.font = .app(.body)
titleLabel.textColor = AppColor.textSecondary
let valueLabel = UILabel()
valueLabel.text = value
valueLabel.font = .systemFont(ofSize: 13)
valueLabel.textColor = AppColor.text333
valueLabel.font = .app(.body)
valueLabel.textColor = AppColor.textPrimary
valueLabel.textAlignment = .right
row.addArrangedSubview(titleLabel)
row.addArrangedSubview(valueLabel)
@ -269,12 +269,12 @@ final class OrderCardCell: UITableViewCell {
row.distribution = .fillEqually
let titleLabel = UILabel()
titleLabel.text = "手机号"
titleLabel.font = .systemFont(ofSize: 13)
titleLabel.textColor = UIColor(hex: 0x6B7280)
titleLabel.font = .app(.body)
titleLabel.textColor = AppColor.textSecondary
let valueButton = UIButton(type: .system)
valueButton.setTitle(OrderPhoneMask.mask(phone), for: .normal)
valueButton.setTitleColor(AppColor.primary, for: .normal)
valueButton.titleLabel?.font = .systemFont(ofSize: 13)
valueButton.titleLabel?.font = .app(.body)
valueButton.contentHorizontalAlignment = .trailing
valueButton.isHidden = hidden || phone.isEmpty
valueButton.addTarget(self, action: #selector(callTapped), for: .touchUpInside)
@ -285,7 +285,7 @@ final class OrderCardCell: UITableViewCell {
private func updateRefinedButtons(isRefined: Int, readOnly: Bool) {
let selectedBorder = AppColor.primary.cgColor
let normalBorder = UIColor(hex: 0xE5E7EB).cgColor
let normalBorder = AppColor.border.cgColor
refinedNeedButton.layer.borderColor = isRefined == 1 ? selectedBorder : normalBorder
refinedNoButton.layer.borderColor = isRefined == 2 ? selectedBorder : normalBorder
refinedNeedButton.isEnabled = !readOnly

View File

@ -6,20 +6,17 @@
import SnapKit
import UIKit
///
/// `AppStatusBadge`
final class OrderStatusBadgeView: UIView {
private let label = UILabel()
private let badge = AppStatusBadge()
override init(frame: CGRect) {
super.init(frame: frame)
label.font = .systemFont(ofSize: 14, weight: .medium)
label.textAlignment = .center
addSubview(label)
label.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 2, left: 4, bottom: 2, right: 4))
addSubview(badge)
badge.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
layer.cornerRadius = 4
clipsToBounds = true
}
@available(*, unavailable)
@ -28,20 +25,6 @@ final class OrderStatusBadgeView: UIView {
}
func apply(status: Int, text: String) {
label.text = text
switch status {
case 18, 20:
label.textColor = UIColor(hex: 0x0073FF)
backgroundColor = UIColor(hex: 0xEFF6FF)
case 30:
label.textColor = UIColor(hex: 0x10B981)
backgroundColor = UIColor(hex: 0xF0FDF4)
case 50:
label.textColor = UIColor(hex: 0xEF4444)
backgroundColor = UIColor(hex: 0xFFE7E7)
default:
label.textColor = UIColor(hex: 0xFF7B00)
backgroundColor = UIColor(hex: 0xFFF0E2)
}
badge.applyOrderStatus(status, text: text)
}
}

View File

@ -30,7 +30,6 @@ final class OrdersViewController: BaseViewController, UITableViewDataSource, UIT
}
override func setupUI() {
view.backgroundColor = UIColor(hex: 0xF5F5F5)
view.addSubview(filterBar)
view.addSubview(listView)
listView.tableView.dataSource = self

View File

@ -6,7 +6,7 @@
import SnapKit
import UIKit
///
/// `AppStatusBadge`
final class ProfileStatusBadgeView: UIView {
enum Style {
@ -20,18 +20,14 @@ final class ProfileStatusBadgeView: UIView {
case bankRejected
}
private let label = UILabel()
private let badge = AppStatusBadge()
override init(frame: CGRect) {
super.init(frame: frame)
label.font = .systemFont(ofSize: 12, weight: .medium)
label.textAlignment = .center
addSubview(label)
label.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 3, left: 8, bottom: 3, right: 8))
addSubview(badge)
badge.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
layer.cornerRadius = 4
clipsToBounds = true
}
@available(*, unavailable)
@ -40,23 +36,15 @@ final class ProfileStatusBadgeView: UIView {
}
func apply(style: Style, text: String) {
label.text = text
switch style {
case .accountActive:
label.textColor = UIColor(hex: 0x22C55E)
backgroundColor = UIColor(hex: 0xF0FDF4)
case .scenic:
label.textColor = AppColor.primary
backgroundColor = UIColor(hex: 0xEFF6FF)
badge.apply(tone: .success, text: text)
case .scenic, .auditApproved, .bankApproved:
badge.apply(tone: .info, text: text)
case .auditPending, .bankPending:
label.textColor = UIColor(hex: 0xFF7B00)
backgroundColor = UIColor(hex: 0xFFF0E2)
case .auditApproved, .bankApproved:
label.textColor = AppColor.primary
backgroundColor = UIColor(hex: 0xEFF6FF)
badge.apply(tone: .warning, text: text)
case .auditRejected, .bankRejected:
label.textColor = UIColor(hex: 0xEF4444)
backgroundColor = UIColor(hex: 0xFFE7E7)
badge.apply(tone: .danger, text: text)
}
}
}

View File

@ -47,27 +47,27 @@ final class StatisticsDailyListView: UIView {
}
private func setupUI() {
backgroundColor = .white
layer.cornerRadius = 12
backgroundColor = AppColor.cardBackground
layer.cornerRadius = AppRadius.lg
clipsToBounds = true
titleLabel.text = "时间范围"
titleLabel.font = .systemFont(ofSize: 14, weight: .bold)
titleLabel.textColor = .black
titleLabel.font = .app(.bodyMedium)
titleLabel.textColor = AppColor.textPrimary
rangeButton.backgroundColor = UIColor(hex: 0xF4F4F4)
rangeButton.layer.cornerRadius = 4
rangeButton.backgroundColor = AppColor.inputBackground
rangeButton.layer.cornerRadius = AppRadius.xs
rangeButton.addTarget(self, action: #selector(rangeTapped), for: .touchUpInside)
rangeLabel.font = .systemFont(ofSize: 14)
rangeLabel.textColor = UIColor(hex: 0x4B5563)
rangeLabel.font = .app(.body)
rangeLabel.textColor = AppColor.textSecondary
rangeLabel.text = "今日"
arrowView.image = UIImage(systemName: "chevron.down")
arrowView.tintColor = UIColor(hex: 0x4B5563)
arrowView.tintColor = AppColor.textSecondary
arrowView.contentMode = .scaleAspectFit
divider.backgroundColor = UIColor(hex: 0xF4F4F4)
divider.backgroundColor = AppColor.inputBackground
tableView.backgroundColor = .clear
tableView.separatorStyle = .none

View File

@ -28,16 +28,16 @@ final class StatisticsPeriodButton: UIButton {
}
private func setupUI() {
titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
layer.cornerRadius = 18
titleLabel?.font = .app(.bodyMedium)
layer.cornerRadius = AppRadius.pill
clipsToBounds = true
contentEdgeInsets = UIEdgeInsets(top: 6, left: 20, bottom: 6, right: 20)
updateAppearance()
}
private func updateAppearance() {
backgroundColor = isSelected ? UIColor(hex: 0x3B82F6) : UIColor(hex: 0xF3F4F6)
setTitleColor(isSelected ? .white : UIColor(hex: 0x4B5563), for: .normal)
backgroundColor = isSelected ? AppColor.primary : AppColor.inputBackground
setTitleColor(isSelected ? .white : AppColor.textSecondary, for: .normal)
titleLabel?.font = .systemFont(ofSize: 14, weight: isSelected ? .semibold : .medium)
}
}

View File

@ -32,13 +32,13 @@ final class StatisticsStatCardView: UIView {
}
private func setupUI() {
layer.cornerRadius = 12
layer.cornerRadius = AppRadius.lg
clipsToBounds = true
titleLabel.font = .systemFont(ofSize: 14)
titleLabel.textColor = UIColor.black.withAlphaComponent(0.6)
titleLabel.font = .app(.body)
titleLabel.textColor = AppColor.textPrimary.withAlphaComponent(0.6)
valueLabel.font = .systemFont(ofSize: 18, weight: .bold)
valueLabel.font = .app(.metric)
valueLabel.numberOfLines = 1
valueLabel.adjustsFontSizeToFitWidth = true
valueLabel.minimumScaleFactor = 0.8
@ -50,16 +50,16 @@ final class StatisticsStatCardView: UIView {
addSubview(iconView)
titleLabel.snp.makeConstraints { make in
make.top.leading.equalToSuperview().inset(12)
make.trailing.lessThanOrEqualTo(iconView.snp.leading).offset(-8)
make.top.leading.equalToSuperview().inset(AppSpacing.sm)
make.trailing.lessThanOrEqualTo(iconView.snp.leading).offset(-AppSpacing.xs)
}
valueLabel.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(3)
make.leading.bottom.equalToSuperview().inset(12)
make.trailing.lessThanOrEqualTo(iconView.snp.leading).offset(-8)
make.leading.bottom.equalToSuperview().inset(AppSpacing.sm)
make.trailing.lessThanOrEqualTo(iconView.snp.leading).offset(-AppSpacing.xs)
}
iconView.snp.makeConstraints { make in
make.trailing.equalToSuperview().inset(8)
make.trailing.equalToSuperview().inset(AppSpacing.xs)
make.centerY.equalToSuperview()
make.size.equalTo(48)
}

View File

@ -18,8 +18,8 @@ final class StatisticsSummaryCardView: UIView {
private let totalAmountCard = StatisticsStatCardView(
title: "订单总金额",
valueColor: UIColor(hex: 0x22C55E),
backgroundColor: UIColor(hex: 0xF0FDF4),
valueColor: AppColor.online,
backgroundColor: AppColor.successBackground,
iconName: "icon_data_statistics_total_amount"
)
private let totalOrderCard = StatisticsStatCardView(
@ -30,20 +30,20 @@ final class StatisticsSummaryCardView: UIView {
)
private let receivedCard = StatisticsStatCardView(
title: "实收金额",
valueColor: UIColor(hex: 0x22C55E),
backgroundColor: UIColor(hex: 0xFFF0E2),
valueColor: AppColor.online,
backgroundColor: AppColor.warningBackground,
iconName: "icon_data_statistics_get_amount"
)
private let averageCard = StatisticsStatCardView(
title: "客单价",
valueColor: UIColor(hex: 0x0073FF),
backgroundColor: UIColor(hex: 0xEFF6FF),
valueColor: AppColor.primary,
backgroundColor: AppColor.primaryLight,
iconName: "icon_data_statistics_average_amount"
)
private let refundCard = StatisticsStatCardView(
title: "退款金额",
valueColor: UIColor(hex: 0xEF4444),
backgroundColor: UIColor(hex: 0xFFE7E7),
valueColor: AppColor.danger,
backgroundColor: AppColor.dangerBackground,
iconName: "icon_data_statistics_refund_amount"
)
@ -69,15 +69,15 @@ final class StatisticsSummaryCardView: UIView {
}
private func setupUI() {
backgroundColor = .white
layer.cornerRadius = 12
backgroundColor = AppColor.cardBackground
layer.cornerRadius = AppRadius.lg
clipsToBounds = true
datePrefixLabel.text = "已选日期:"
datePrefixLabel.font = .systemFont(ofSize: 14)
datePrefixLabel.textColor = AppColor.text333
datePrefixLabel.font = .app(.body)
datePrefixLabel.textColor = AppColor.textPrimary
dateValueLabel.font = .systemFont(ofSize: 14)
dateValueLabel.font = .app(.body)
dateValueLabel.textColor = AppColor.primary
dateValueLabel.numberOfLines = 1
@ -95,17 +95,17 @@ final class StatisticsSummaryCardView: UIView {
let row1 = UIStackView(arrangedSubviews: [totalOrderCard, receivedCard])
row1.axis = .horizontal
row1.spacing = 12
row1.spacing = AppSpacing.sm
row1.distribution = .fillEqually
let row2 = UIStackView(arrangedSubviews: [averageCard, refundCard])
row2.axis = .horizontal
row2.spacing = 12
row2.spacing = AppSpacing.sm
row2.distribution = .fillEqually
let cardStack = UIStackView(arrangedSubviews: [totalAmountCard, row1, row2])
cardStack.axis = .vertical
cardStack.spacing = 12
cardStack.spacing = AppSpacing.sm
let dateRow = UIStackView(arrangedSubviews: [datePrefixLabel, dateValueLabel, UIView()])
dateRow.axis = .horizontal
@ -114,11 +114,11 @@ final class StatisticsSummaryCardView: UIView {
let contentStack = UIStackView(arrangedSubviews: [dateRow, periodStack, cardStack])
contentStack.axis = .vertical
contentStack.spacing = 12
contentStack.spacing = AppSpacing.sm
addSubview(contentStack)
contentStack.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(16)
make.edges.equalToSuperview().inset(AppSpacing.md)
}
}

View File

@ -23,7 +23,7 @@ final class StatisticsViewController: BaseViewController {
}
override func setupUI() {
view.backgroundColor = UIColor(hex: 0xF7FAFF)
view.backgroundColor = AppColor.pageBackgroundSoft
view.addSubview(summaryCardView)
view.addSubview(dailyListView)
@ -31,13 +31,13 @@ final class StatisticsViewController: BaseViewController {
override func setupConstraints() {
summaryCardView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide).offset(16)
make.leading.trailing.equalToSuperview().inset(16)
make.top.equalTo(view.safeAreaLayoutGuide).offset(AppSpacing.md)
make.leading.trailing.equalToSuperview().inset(AppSpacing.screenHorizontalInset)
}
dailyListView.snp.makeConstraints { make in
make.top.equalTo(summaryCardView.snp.bottom).offset(16)
make.leading.trailing.equalToSuperview().inset(16)
make.bottom.equalTo(view.safeAreaLayoutGuide).inset(16)
make.top.equalTo(summaryCardView.snp.bottom).offset(AppSpacing.md)
make.leading.trailing.equalToSuperview().inset(AppSpacing.screenHorizontalInset)
make.bottom.equalTo(view.safeAreaLayoutGuide).inset(AppSpacing.md)
}
}