iOS 门店营收表格功能实现方案
核心功能需求
- 数据展示:表格形式展示门店/日期维度的营收数据
- 排序功能:支持按营收金额、增长率等排序
- 筛选功能:按日期范围/门店/区域筛选
- 交互操作:点击查看详情、数据刷新
- 数据可视化:关键指标图表展示
技术实现方案(UIKit)
1. 数据模型设计
struct StoreRevenue {
let storeId: String
let storeName: String
let region: String
var dailyRevenue: [Date: Double] // 日期-营收键值对
var totalRevenue: Double { dailyRevenue.values.reduce(0, +) }
}
// 日期范围结构体
struct DateRange {
let startDate: Date
let endDate: Date
}
2. 表格视图实现(UITableView)
class RevenueViewController: UIViewController {
private let tableView = UITableView()
private var revenues: [StoreRevenue] = []
private var filteredRevenues: [StoreRevenue] = []
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
loadData()
}
private func setupTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(RevenueCell.self, forCellReuseIdentifier: "RevenueCell")
// 添加下拉刷新
tableView.refreshControl = UIRefreshControl()
tableView.refreshControl?.addTarget(self, action: #selector(refreshData), for: .valueChanged)
}
@objc private func refreshData() {
// 实现数据刷新逻辑
}
}
3. 自定义表格单元格
class RevenueCell: UITableViewCell {
static let identifier = "RevenueCell"
private let nameLabel = UILabel()
private let revenueLabel = UILabel()
private let trendIndicator = UIImageView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
private func setupViews() {
// 布局代码
contentView.addSubview(nameLabel)
contentView.addSubview(revenueLabel)
contentView.addSubview(trendIndicator)
// 自动布局约束...
}
func configure(with revenue: StoreRevenue) {
nameLabel.text = revenue.storeName
revenueLabel.text = formatCurrency(revenue.totalRevenue)
trendIndicator.image = revenue.growthRate >= 0 ? UIImage(systemName: "arrow.up") : UIImage(systemName: "arrow.down")
}
private func formatCurrency(_ amount: Double) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter.string(from: NSNumber(value: amount)) ?? ""
}
}
4. 筛选控制器实现
class FilterViewController: UIViewController {
var onFilterApplied: ((DateRange, String?) -> Void)?
private let datePicker = UIDatePicker()
private let regionPicker = UIPickerView()
private let regions = ["All", "North", "South", "East", "West"]
override func viewDidLoad() {
super.viewDidLoad()
setupFilterUI()
}
private func setupFilterUI() {
// 日期范围选择器
datePicker.datePickerMode = .date
datePicker.preferredDatePickerStyle = .compact
// 区域选择器
regionPicker.delegate = self
regionPicker.dataSource = self
// 应用按钮
let applyButton = UIButton(type: .system)
applyButton.setTitle("Apply Filters", for: .normal)
applyButton.addTarget(self, action: #selector(applyFilters), for: .touchUpInside)
// 添加视图和约束...
}
@objc private func applyFilters() {
let selectedRegion = regions[regionPicker.selectedRow(inComponent: 0)]
let dateRange = DateRange(startDate: ..., endDate: ...)
onFilterApplied?(dateRange, selectedRegion == "All" ? nil : selectedRegion)
dismiss(animated: true)
}
}
5. 排序功能实现
extension RevenueViewController {
enum SortOption {
case nameAscending, nameDescending
case revenueAscending, revenueDescending
case growthRateAscending, growthRateDescending
}
func sortData(by option: SortOption) {
filteredRevenues.sort {
switch option {
case .nameAscending: return $0.storeName < $1.storeName
case .nameDescending: return $0.storeName > $1.storeName
case .revenueAscending: return $0.totalRevenue < $1.totalRevenue
case .revenueDescending: return $0.totalRevenue > $1.totalRevenue
case .growthRateAscending: return $0.growthRate < $1.growthRate
case .growthRateDescending: return $0.growthRate > $1.growthRate
}
}
tableView.reloadData()
}
}
6. 数据可视化(使用Charts框架)
import Charts
class RevenueChartView: UIView {
private let barChart = BarChartView()
func configure(with revenues: [StoreRevenue]) {
var entries = [BarChartDataEntry]()
for (index, revenue) in revenues.enumerated() {
let entry = BarChartDataEntry(
x: Double(index),
y: revenue.totalRevenue
)
entries.append(entry)
}
let dataSet = BarChartDataSet(entries: entries, label: "门店营收")
dataSet.colors = [.systemBlue]
let data = BarChartData(dataSet: dataSet)
barChart.data = data
// 配置图表样式
barChart.xAxis.valueFormatter = IndexAxisValueFormatter(values: revenues.map { $0.storeName })
barChart.xAxis.granularity = 1
barChart.leftAxis.axisMinimum = 0
barChart.animate(yAxisDuration: 1.0)
}
}
7. 性能优化策略
- 数据分页加载:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == filteredRevenues.count - 2 {
loadMoreData()
}
}
- 图片/图标缓存:
let imageCache = NSCache<NSString, UIImage>()
func loadStoreImage(for storeId: String) {
if let cachedImage = imageCache.object(forKey: storeId as NSString) {
// 使用缓存图片
return
}
// 异步加载网络图片
DispatchQueue.global().async {
if let image = // 下载图片 {
imageCache.setObject(image, forKey: storeId as NSString)
DispatchQueue.main.async {
// 更新UI
}
}
}
}
- 后台数据处理:
DispatchQueue.global(qos: .userInitiated).async {
let processedData = self.processRawData(rawData)
DispatchQueue.main.async {
self.revenues = processedData
self.tableView.reloadData()
}
}
关键注意事项
-
数据安全:
- 使用HTTPS传输敏感营收数据
- 本地缓存加密(使用Keychain存储认证token)
-
空状态处理:
func showEmptyState() { let emptyView = EmptyStateView() emptyView.configure( title: "无营收数据", message: "当前筛选条件下没有找到门店营收记录", image: UIImage(named: "empty-revenue") ) tableView.backgroundView = emptyView }
-
国际化支持:
- 使用NSLocalizedString处理多语言
- 动态适配货币符号和数字格式
-
无障碍访问:
revenueLabel.accessibilityLabel = "总营收: \(formatCurrency(revenue.totalRevenue))" revenueLabel.accessibilityTraits = .staticText
-
深色模式适配:
- 使用系统颜色(.label, .systemBackground)
- 提供深色模式下的图表配色方案
扩展功能建议
-
数据导出:
- 支持导出CSV/PDF格式报表
- 使用UIActivityViewController实现分享功能
-
实时更新:
// 使用WebSocket接收实时数据更新 socket.on("revenue_update") { [weak self] data in self?.handleRevenueUpdate(data) }
-
多维度分析:
- 添加切换视图:日/周/月视图
- 同比/环比分析功能
-
异常检测:
func detectAnomalies() { // 使用机器学习框架检测异常波动 CoreMLHelper.detectAnomalies(in: revenues) }
-
离线模式:
- 使用CoreData缓存数据
- 实现本地数据修改同步队列
此实现方案提供了高性能的表格展示、灵活的数据操作和良好的用户体验,可根据实际业务需求调整具体实现细节。建议结合AutoLayout实现响应式布局,确保在各种设备尺寸上正常显示。