mirror of
https://github.com/VGEAREN/Stockbar.git
synced 2026-10-06 14:33:11 +08:00
v1.2.1: Add sort settings (change% asc/desc, US session/regular price mode)
- Add SortRule enum (涨跌幅升序/降序) and USPriceMode enum (时段价格/盘中价) - Settings page: sort rule segmented picker + US price mode picker - Toolbar sort button applies configured rule; off = default order - US stocks: session price mode uses extendedPrice for change% calculation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
94b14693d5
commit
c7b19e4f49
@@ -22,6 +22,30 @@ enum DisplayCurrency: String, Codable, CaseIterable {
|
||||
}
|
||||
}
|
||||
|
||||
enum SortRule: String, Codable, CaseIterable {
|
||||
case changeDesc = "changeDesc"
|
||||
case changeAsc = "changeAsc"
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .changeDesc: return "涨跌幅降序"
|
||||
case .changeAsc: return "涨跌幅升序"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum USPriceMode: String, Codable, CaseIterable {
|
||||
case sessionPrice = "session"
|
||||
case regularPrice = "regular"
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .sessionPrice: return "当前价(时段价格)"
|
||||
case .regularPrice: return "盘中价"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ColorTheme: String, Codable, CaseIterable {
|
||||
case chinese = "chinese" // 红涨绿跌
|
||||
case western = "western" // 绿涨红跌
|
||||
@@ -39,6 +63,8 @@ struct AppSettings {
|
||||
var refreshInterval: Int = 5
|
||||
var colorScheme: ColorTheme = .chinese
|
||||
var displayCurrency: DisplayCurrency = .cny
|
||||
var sortRule: SortRule = .changeDesc
|
||||
var usPriceMode: USPriceMode = .sessionPrice
|
||||
|
||||
static let validRefreshIntervals = [3, 5, 10, 30]
|
||||
|
||||
@@ -49,7 +75,7 @@ struct AppSettings {
|
||||
// MARK: - Codable(容错:缺失字段使用默认值)
|
||||
extension AppSettings: Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case statusBarStockId, refreshInterval, colorScheme, displayCurrency
|
||||
case statusBarStockId, refreshInterval, colorScheme, displayCurrency, sortRule, usPriceMode
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
@@ -58,5 +84,7 @@ extension AppSettings: Codable {
|
||||
refreshInterval = (try? c.decodeIfPresent(Int.self, forKey: .refreshInterval)) ?? 5
|
||||
colorScheme = (try? c.decodeIfPresent(ColorTheme.self, forKey: .colorScheme)) ?? .chinese
|
||||
displayCurrency = (try? c.decodeIfPresent(DisplayCurrency.self, forKey: .displayCurrency)) ?? .cny
|
||||
sortRule = (try? c.decodeIfPresent(SortRule.self, forKey: .sortRule)) ?? .changeDesc
|
||||
usPriceMode = (try? c.decodeIfPresent(USPriceMode.self, forKey: .usPriceMode)) ?? .sessionPrice
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,29 @@ struct DropdownView: View {
|
||||
[Market.aStock, .hkStock, .usStock].compactMap { market in
|
||||
var s = appState.stocks.filter { $0.market == market }
|
||||
if sortByChange {
|
||||
s.sort {
|
||||
let a = appState.quotes[$0.id]?.changePercent ?? -Double.infinity
|
||||
let b = appState.quotes[$1.id]?.changePercent ?? -Double.infinity
|
||||
return a > b
|
||||
let rule = appState.config.sortRule
|
||||
let usMode = appState.config.usPriceMode
|
||||
s.sort { a, b in
|
||||
let qa = appState.quotes[a.id]
|
||||
let qb = appState.quotes[b.id]
|
||||
let va = sortValue(quote: qa, market: market, mode: usMode)
|
||||
let vb = sortValue(quote: qb, market: market, mode: usMode)
|
||||
return rule == .changeDesc ? va > vb : va < vb
|
||||
}
|
||||
}
|
||||
return s.isEmpty ? nil : (market, s)
|
||||
}
|
||||
}
|
||||
|
||||
private func sortValue(quote: Quote?, market: Market, mode: USPriceMode) -> Double {
|
||||
guard let q = quote else { return -Double.infinity }
|
||||
if market == .usStock && mode == .sessionPrice, let ext = q.extendedPrice {
|
||||
// 用时段价格算涨跌幅:(盘外价 - 正盘价) / 正盘价
|
||||
return q.price > 0 ? (ext - q.price) / q.price * 100 : q.changePercent
|
||||
}
|
||||
return q.changePercent
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
if showSettings {
|
||||
|
||||
@@ -54,6 +54,30 @@ struct SettingsView: View {
|
||||
}.pickerStyle(.menu)
|
||||
}
|
||||
|
||||
// 排序规则
|
||||
section("排序规则") {
|
||||
Picker("", selection: Binding(
|
||||
get: { appState.config.sortRule },
|
||||
set: { appState.config.sortRule = $0 }
|
||||
)) {
|
||||
ForEach(SortRule.allCases, id: \.rawValue) { rule in
|
||||
Text(rule.displayName).tag(rule)
|
||||
}
|
||||
}.pickerStyle(.segmented)
|
||||
}
|
||||
|
||||
// 美股排序价格
|
||||
section("美股排序价格") {
|
||||
Picker("", selection: Binding(
|
||||
get: { appState.config.usPriceMode },
|
||||
set: { appState.config.usPriceMode = $0 }
|
||||
)) {
|
||||
ForEach(USPriceMode.allCases, id: \.rawValue) { mode in
|
||||
Text(mode.displayName).tag(mode)
|
||||
}
|
||||
}.pickerStyle(.segmented)
|
||||
}
|
||||
|
||||
// 刷新间隔
|
||||
section("刷新间隔") {
|
||||
Picker("", selection: Binding(
|
||||
|
||||
Reference in New Issue
Block a user