个人信息页新增身高体重
authorliangzhenying <867174124@qq.com>
Mon, 26 Jan 2026 06:12:24 +0000 (14:12 +0800)
committerliangzhenying <867174124@qq.com>
Mon, 26 Jan 2026 06:12:24 +0000 (14:12 +0800)
14 files changed:
ShenYin/ShenYin/Launch/SceneDelegate.swift
ShenYin/ShenYin/UI/Mine/AboutVC.swift
ShenYin/ShenYin/UI/Mine/AccountSetVC.swift
ShenYin/ShenYin/UI/Mine/Instructions/InstructionsVC.swift
ShenYin/ShenYin/UI/Mine/MoreSetVC.swift
ShenYin/ShenYin/UI/Mine/UserInfo/CustomCell.swift [moved from ShenYin/ShenYin/UI/Mine/UserInfo/CustomCell1.swift with 57% similarity]
ShenYin/ShenYin/UI/Mine/UserInfo/UserInfoVC.swift
ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetVC.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/Sport/Sport/Set/SwitchCell.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/common/CustomButton.swift
ShenYin/ShenYin/UI/common/pop/CustomPicker2.swift [new file with mode: 0644]
ShenYin/ShenYin/UI/common/pop/CustomPicker3.swift [new file with mode: 0644]
ShenYin/ShenYin/lan/chinese.swift
document/深音APP 骨传导耳机运动功能定义-0122-V1.0.docx [new file with mode: 0644]

index 13273e9..fa4c4d0 100644 (file)
@@ -19,8 +19,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
         App.shared.window = window
 //        window?.rootViewController = BaseTC()
 //        MapManager.shared.setupSdk()
-//        window?.rootViewController = UINavigationController(rootViewController: ReadyVC())
-        AppLaunch.start()
+        window?.rootViewController = UINavigationController(rootViewController: SportSetVC())
+//        AppLaunch.start()
     }
 
     func sceneDidDisconnect(_ scene: UIScene) {
index 4f8bca7..1db3c4e 100644 (file)
@@ -23,7 +23,7 @@ class AboutVC: BaseVC{
             v.rowHeight = 52
             v.sectionHeaderHeight = 52
             v.separatorStyle = .none
-            v.register(CustomCell1.self, forCellReuseIdentifier: CustomCell1.reuseID)
+            v.register(CustomCell.self, forCellReuseIdentifier: CustomCell.reuseID)
             v.register(CustomSectionHeader.self, forHeaderFooterViewReuseIdentifier: CustomSectionHeader.reuseID)
             v.tableHeaderView = AboutHeaderView(frame: CGRect(x: 0, y: 0, width: kScreenWid, height: 175))
         }
@@ -48,7 +48,7 @@ extension AboutVC:UITableViewDelegate,UITableViewDataSource {
     }
 
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        let cell = CustomCell1.cell(withTableView: tableView, indexPath: indexPath)
+        let cell = CustomCell.cell(withTableView: tableView, indexPath: indexPath)
         cell.type = list[indexPath.section][indexPath.row]
         let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
         if indexPath.row == 0 {
index 5ca7cf7..1809ed8 100644 (file)
@@ -24,7 +24,7 @@ class AccountSetVC: BaseVC{
             v.rowHeight = 52
             v.sectionHeaderHeight = 52
             v.separatorStyle = .none
-            v.register(CustomCell1.self, forCellReuseIdentifier: CustomCell1.reuseID)
+            v.register(CustomCell.self, forCellReuseIdentifier: CustomCell.reuseID)
             v.register(CustomSectionHeader.self, forHeaderFooterViewReuseIdentifier: CustomSectionHeader.reuseID)
             v.tableHeaderView = header
         }
@@ -59,7 +59,7 @@ extension AccountSetVC:UITableViewDelegate,UITableViewDataSource {
         return view
     }
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        let cell = CustomCell1.cell(withTableView: tableView, indexPath: indexPath)
+        let cell = CustomCell.cell(withTableView: tableView, indexPath: indexPath)
         let list = list[indexPath.section].list
         cell.type = list[indexPath.row]
         let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
index b974a77..e553155 100644 (file)
@@ -30,7 +30,7 @@ class InstructionsVC: BaseVC{
             v.rowHeight = 52
             v.sectionHeaderHeight = 52
             v.separatorStyle = .none
-            v.register(CustomCell1.self, forCellReuseIdentifier: CustomCell1.reuseID)
+            v.register(CustomCell.self, forCellReuseIdentifier: CustomCell.reuseID)
 //            v.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: kScreenHei, height: 20))
         }
                 
@@ -65,7 +65,7 @@ extension InstructionsVC:UITableViewDelegate,UITableViewDataSource {
         return searchList.count
     }
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        let cell = CustomCell1.cell(withTableView: tableView, indexPath: indexPath)
+        let cell = CustomCell.cell(withTableView: tableView, indexPath: indexPath)
         cell.titleLabel.text = searchList[indexPath.row].name
         let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
         if indexPath.row == 0 {
index f40599f..d74f9c9 100644 (file)
@@ -28,7 +28,7 @@ class MoreSetVC: BaseVC{
             v.rowHeight = 52
             v.sectionHeaderHeight = 52
             v.separatorStyle = .none
-            v.register(CustomCell1.self, forCellReuseIdentifier: CustomCell1.reuseID)
+            v.register(CustomCell.self, forCellReuseIdentifier: CustomCell.reuseID)
             v.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: kScreenHei, height: 20))
             v.bounces = false
         }
@@ -106,7 +106,7 @@ extension MoreSetVC:UITableViewDelegate,UITableViewDataSource {
         return list.count
     }
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        let cell = CustomCell1.cell(withTableView: tableView, indexPath: indexPath)
+        let cell = CustomCell.cell(withTableView: tableView, indexPath: indexPath)
         cell.type = list[indexPath.row]
         let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
         if indexPath.row == 0 {
@@ -1,5 +1,5 @@
 //
-//  CustomCell1.swift
+//  CustomCell.swift
 //  ShenYin
 //
 //  Created by liangshi on 2025/8/25.
@@ -8,9 +8,12 @@ public enum CustomCellType: Int {
     case avatar
     case nickName
     case gender
+    case birthday
+    case height
+    case weight
+    case hobbies
     case area
-    
-    
+         
     case account
     case phone
     case email
@@ -29,6 +32,19 @@ public enum CustomCellType: Int {
     
     case deregister
     
+    case light
+    case voiceReport
+    case lcReport
+    case xlfdReport
+    case guideReport
+    case metronome
+    case metronomeRate
+    case metronomeAudio
+    case background
+    case permisson
+    case realData
+
+    
     var title:String{
         switch self {
         case .avatar:
@@ -37,6 +53,14 @@ public enum CustomCellType: Int {
             return "昵称".l10n
         case .gender:
             return "性别".l10n
+        case .birthday:
+            return "生日".l10n
+        case .height:
+            return "身高".l10n
+        case .weight:
+            return "体重".l10n
+        case .hobbies:
+            return "运动偏好".l10n
         case .area:
             return "国家与地区".l10n
             
@@ -71,13 +95,35 @@ public enum CustomCellType: Int {
             
         case .deregister:
             return "注销账号".l10n
+            
+        case .light:
+            return "灯带设置".l10n
+        case .voiceReport:
+            return "语音播报".l10n
+        case .lcReport:
+            return "里程播报".l10n
+        case .xlfdReport:
+            return "训练分段播报".l10n
+        case .guideReport:
+            return "指导播报".l10n
+        case .metronome:
+            return "节拍器".l10n
+        case .metronomeRate:
+            return "节拍器频率".l10n
+        case .metronomeAudio:
+            return "节拍器声音".l10n
+        case .background:
+            return "后台保护".l10n
+        case .permisson:
+            return "运动权限".l10n
+        case .realData:
+            return "运动实况".l10n
         }
     }
-    
 }
 
 
-class CustomCell1: BaseTableViewCell {
+class CustomCell: BaseTableViewCell {
     let bgView = UIView()
     let titleLabel = UILabel()
     let nextIcon = UIImageView()
@@ -89,7 +135,6 @@ class CustomCell1: BaseTableViewCell {
             titleLabel.text = type.title
             valueIcon.isHidden = type != .avatar
             
-            
             if type == .nickName || type == .gender || type == .area {
                 valueLabel.isHidden = false
             }
@@ -180,6 +225,83 @@ class CustomCell1: BaseTableViewCell {
             m.left.equalToSuperview().inset(90)
         }
     }
-    
+}
+
 
+
+class SwitchCell: BaseTableViewCell {
+    let bgView = UIView()
+    let titleLabel = UILabel()
+    let detailLabel = UILabel()
+    let iSwitch = UISwitch()
+    var hasDetail:Bool = true{
+        didSet{
+            detailLabel.isHidden = !hasDetail
+            if hasDetail {
+                titleLabel.snp.makeConstraints { (m) in
+                    m.top.equalToSuperview().inset(16)
+                    m.left.equalToSuperview().offset(16)
+                }
+            }
+            else{
+                titleLabel.snp.makeConstraints { (m) in
+                    m.centerY.equalToSuperview()
+                    m.left.equalToSuperview().offset(16)
+                }
+                
+            }
+        }
+    }
+    
+    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+        super.init(style: style, reuseIdentifier: reuseIdentifier)
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    override func setupViews() {
+        contentView.addSubview(bgView)
+        bgView.addSubviews([titleLabel,detailLabel,iSwitch])
+        
+        backgroundColor = .color(.bgColor)
+        contentView.backgroundColor = .clear
+        selectionStyle = .none
+        bgView.do { v in
+            v.backgroundColor = .color(.cellBg)
+        }
+        titleLabel.do { (v) in
+            v.textColor = .color(.first)
+            v.font = .bold(14)
+            v.textAlignment = .left
+        }
+        detailLabel.do { (v) in
+            v.textColor = .color(.secondary)
+            v.font = .regular(10)
+            v.textAlignment = .left
+            v.isHidden = !hasDetail
+        }
+    }
+    
+    override func setupLayout() {
+        bgView.snp.makeConstraints { (m) in
+            m.left.right.equalToSuperview().inset(18)
+            m.top.equalToSuperview()
+            m.bottom.equalToSuperview()
+        }
+        titleLabel.snp.makeConstraints { (m) in
+            m.top.equalToSuperview().inset(16)
+            m.left.equalToSuperview().offset(16)
+        }
+        detailLabel.snp.makeConstraints { (m) in
+            m.bottom.equalToSuperview().inset(16)
+            m.left.equalToSuperview().offset(16)
+        }
+        iSwitch.snp.makeConstraints { (m) in
+            m.width.equalTo(50)
+            m.height.equalToSuperview()
+            m.right.equalToSuperview().inset(15)
+            m.centerY.equalToSuperview()
+        }
+    }
 }
index b7d72a4..c0742e0 100644 (file)
@@ -8,7 +8,7 @@
 import UIKit
 class UserInfoVC: BaseVC{
     let tableView = UITableView(frame: .zero, style: .plain)
-    let list:[CustomCellType] = [.avatar,.nickName,.gender,.area]
+    let list:[CustomCellType] = [.avatar,.nickName,.gender,.birthday,.height,.weight,.hobbies,.area]
     let imgPicker = ImagePickerManager()
     
     override func viewDidLoad() {
@@ -24,7 +24,7 @@ class UserInfoVC: BaseVC{
             v.rowHeight = 52
             v.sectionHeaderHeight = 52
             v.separatorStyle = .none
-            v.register(CustomCell1.self, forCellReuseIdentifier: CustomCell1.reuseID)
+            v.register(CustomCell.self, forCellReuseIdentifier: CustomCell.reuseID)
         }
         view.addSubview(tableView)
     }
@@ -47,7 +47,7 @@ class UserInfoVC: BaseVC{
         }
 
         func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-            let cell = CustomCell1.cell(withTableView: tableView, indexPath: indexPath)
+            let cell = CustomCell.cell(withTableView: tableView, indexPath: indexPath)
             cell.type = list[indexPath.row]
             let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
             if indexPath.row == 0 {
@@ -73,6 +73,18 @@ class UserInfoVC: BaseVC{
             case .area:
                 areaAction()
                 break
+            case .birthday:
+                birthdayAction()
+                break
+            case .height:
+                heightAction()
+                break
+            case .weight:
+                weightAction()
+                break
+            case .hobbies:
+                hobiesAction()
+                break
             default:
                 break
             }
@@ -81,6 +93,45 @@ class UserInfoVC: BaseVC{
 
 
 extension UserInfoVC{
+    func birthdayAction(){
+        let v = CustomPicker3(title: "日期".l10n)
+//        v.selectedIndex = 50
+        v.show()
+        v.didSelected = {[weak self] (date) in
+            guard let self = self else { return  }
+            
+        }
+    }
+    func heightAction(){
+        let list = Array(stride(from: 100, to: 250, by: 1)).map { String($0) + "cm" }
+        let v = CustomPicker2(title: "身高".l10n, list:list )
+        v.selectedIndex = 50
+        v.show()
+        v.didSelected = {[weak self] (index,value) in
+            guard let self = self else { return  }
+
+        }
+    }
+    func weightAction(){
+        let list = Array(stride(from: 20, to: 200, by: 1)).map { String($0) + "kg" }
+        let v = CustomPicker2(title: "身高".l10n, list:list )
+        v.selectedIndex = 0
+        v.show()
+        v.didSelected = {[weak self] (index,value) in
+            guard let self = self else { return  }
+            
+        }
+    }
+    func hobiesAction(){
+        let list = ["入门".l10n,"规律运动".l10n,"资深达人".l10n]
+        let v = CustomPicker2(title: "运动偏好".l10n, list:list )
+        v.selectedIndex = 0
+        v.show()
+        v.didSelected = {[weak self] (index,value) in
+            guard let self = self else { return  }
+            
+        }
+    }
     func genderAction(){
         let vc = GenderVC()
         vc.didSelected = {[weak  self] model in
diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetVC.swift b/ShenYin/ShenYin/UI/Sport/Sport/Set/SportSetVC.swift
new file mode 100644 (file)
index 0000000..49c1ad1
--- /dev/null
@@ -0,0 +1,93 @@
+//
+//  SportSetVC.swift
+//  ShenYin
+//
+//  Created by liangshi on 2026/1/23.
+//,,
+
+
+import UIKit
+class SportSetVC: BaseVC{
+    let tableView = UITableView(frame: .zero, style: .plain)
+    let list:[(title:String,list:[CustomCellType])] = [("".l10n,[.light,.voiceReport,.lcReport,.xlfdReport,.guideReport,.metronome,.metronomeRate,.metronomeAudio,.background,.permisson]),("组队设置".l10n,[.realData]),]
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        // Do any additional setup after loading the view.
+    }
+    override func setupViews() {
+        self.navBar.title = "设置".l10n
+        tableView.do { v in
+            v.backgroundColor = .color(.bgColor)
+            v.delegate = self
+            v.dataSource = self
+            v.rowHeight = 52
+            v.sectionHeaderHeight = 52
+            v.separatorStyle = .none
+            v.register(CustomCell.self, forCellReuseIdentifier: CustomCell.reuseID)
+        }
+        
+        view.addSubview(tableView)
+    }
+    override func setupLayout() {
+        tableView.snp.makeConstraints { make in
+            make.left.right.bottom.equalToSuperview()
+            make.top.equalTo(kNavBtm)
+        }
+    }
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+//        CacheManager.shared.user.tokenIsValid(){[weak self] sucess in
+//            self?.header.updateUI()
+//        }
+    }
+}
+
+extension SportSetVC:UITableViewDelegate,UITableViewDataSource {
+    func numberOfSections(in tableView: UITableView) -> Int {
+        return list.count
+    }
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return list[section].list.count
+    }
+    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
+        let view = CustomSectionHeader.header(withTableView: tableView)
+        view.titleLabel.text = list[section].title
+        view.titleLabel.textColor = .color(.secondary)
+        return view
+    }
+    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+        if list[section].title.isEmpty {
+            return 0
+        }
+        else{
+            return 52
+        }
+    }
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = CustomCell.cell(withTableView: tableView, indexPath: indexPath)
+        let list = list[indexPath.section].list
+        cell.type = list[indexPath.row]
+        let rect = CGRect(x: 0, y: 0, width: kScreenWid - 36, height: 52)
+        if indexPath.row == 0 {
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 12, height: 12))
+        }
+        else if indexPath.row == list.count - 1{
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 12, height: 12))
+        }
+        else{
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0))
+        }
+        if list.count == 1 {
+            cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 12, height: 12))
+        }
+        return cell
+    }
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        let data = list[indexPath.section].list[indexPath.row]
+        switch data {
+        default:
+            break
+        }
+    }
+}
diff --git a/ShenYin/ShenYin/UI/Sport/Sport/Set/SwitchCell.swift b/ShenYin/ShenYin/UI/Sport/Sport/Set/SwitchCell.swift
new file mode 100644 (file)
index 0000000..b0501b7
--- /dev/null
@@ -0,0 +1,8 @@
+//
+//  Untitled.swift
+//  ShenYin
+//
+//  Created by liangshi on 2026/1/23.
+//
+
+
index 91c3c9f..a56ca70 100644 (file)
@@ -20,7 +20,7 @@ class CustomButton: UIButton {
         if CCDevice.isJoy{
             setTitle(title, for: .normal)
             addTarget(target, action: action, for: .touchUpInside)
-            layer.cornerRadius = 23
+            layer.cornerRadius = 12
             layer.masksToBounds = true
             titleLabel?.font = font
             
diff --git a/ShenYin/ShenYin/UI/common/pop/CustomPicker2.swift b/ShenYin/ShenYin/UI/common/pop/CustomPicker2.swift
new file mode 100644 (file)
index 0000000..c8e46f2
--- /dev/null
@@ -0,0 +1,132 @@
+//
+//  Untitled.swift
+//  ShenYin
+//
+//  Created by liangshi on 2026/1/23.
+//
+
+import UIKit
+
+class CustomPicker2: BasePresentView {
+    private let titleLabel = UILabel()
+    private let btn = UIButton(type: .custom)
+    private let okBtn = CustomButton()
+    private let pickerView = UIPickerView()
+    var list: [String] = []
+    var disableTitle: String = "" {
+        didSet {
+            pickerView.reloadAllComponents()
+        }
+    }
+    var selectedIndex = 0 {
+        didSet {
+            // 选中当前索引对应的行
+            pickerView.selectRow(selectedIndex, inComponent: 0, animated: true)
+        }
+    }
+    var didSelected: ((Int, String) -> Void)?
+    private var title: String = ""
+    
+    init(title: String, list: [String]) {
+        self.title = title
+        self.list = list
+        let h = CGFloat(50 + 220 + kSafeBtm)
+        super.init(contentHeight: h)
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func setupSubView() {
+        super.setupSubView()
+        contentView.addSubviews([titleLabel, btn,okBtn, pickerView])
+    }
+    
+    override func setupLayout() {
+        super.setupLayout()
+        titleLabel.snp.makeConstraints { (m) in
+            m.top.equalToSuperview()
+            m.left.right.equalToSuperview().inset(60)
+            m.height.equalTo(50)
+        }
+        btn.snp.makeConstraints { (m) in
+            m.centerY.equalTo(titleLabel)
+            m.right.equalToSuperview()
+            m.size.equalTo(60)
+        }
+        okBtn.snp.makeConstraints { make in
+            make.bottom.equalToSuperview().inset(32)
+            make.left.right.equalToSuperview().inset(51)
+            make.height.equalTo(46)
+        }
+        pickerView.snp.makeConstraints { make in
+            make.left.right.equalToSuperview()
+            make.top.equalToSuperview().inset(50)
+            make.bottom.equalTo(okBtn.snp.top).offset(-10)
+        }
+
+    }
+    
+    override func setupStyle() {
+        super.setupStyle()
+        contentView.backgroundColor = .color(.cellBg)
+        titleLabel.do { (v) in
+            v.textColor = .color(.secondary)
+            v.font = .semiBold(16)
+            v.textAlignment = .center
+            v.text = title
+        }
+        btn.do { v in
+            v.setImage(UIImage(named: "close_icon"), for: .normal)
+            v.addTarget(self, action: #selector(closeAction), for: .touchUpInside)
+        }
+        pickerView.do { v in
+            v.backgroundColor = .color(.cellBg)
+            v.delegate = self
+            v.dataSource = self
+        }
+        okBtn.set(title: "确定".l10n,style: .theme1, target: self, action: #selector(okAction))
+    }
+    
+    @objc func closeAction() {
+        hide()
+    }
+    @objc func okAction(){
+        didSelected?(selectedIndex, list[selectedIndex])
+        hide()
+    }
+}
+
+extension CustomPicker2: UIPickerViewDelegate, UIPickerViewDataSource {
+    func numberOfComponents(in pickerView: UIPickerView) -> Int {
+        return 1
+    }
+    
+    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
+        return list.count
+    }
+    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
+        return 50
+    }
+    
+    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
+        let item = list[row]
+        // 如果是禁用项,则显示不同样式(这里通过返回带删除线的文本表示)
+        if item == disableTitle {
+            return "❌\(item)"
+        }
+        return item
+    }
+    
+    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
+        if list[row] == disableTitle {
+            // 如果选择的是禁用项,则恢复到之前的选择
+            pickerView.selectRow(selectedIndex, inComponent: 0, animated: true)
+            return
+        }
+        selectedIndex = row
+        didSelected?(selectedIndex, list[selectedIndex])
+//        hide()
+    }
+}
diff --git a/ShenYin/ShenYin/UI/common/pop/CustomPicker3.swift b/ShenYin/ShenYin/UI/common/pop/CustomPicker3.swift
new file mode 100644 (file)
index 0000000..78dea69
--- /dev/null
@@ -0,0 +1,110 @@
+import UIKit
+
+class CustomPicker3: BasePresentView {
+    private let titleLabel = UILabel()
+    private let btn = UIButton(type: .custom)
+    private let okBtn = CustomButton()
+    private let datePicker = UIDatePicker()
+    var selectedDate = Date() {
+        didSet {
+            datePicker.date = selectedDate
+        }
+    }
+    var didSelected: ((Date) -> Void)?
+    private var title: String = ""
+    
+    init(title: String, defaultDate: Date = createDate(year: 2000, month: 1, day: 1)) {
+        self.title = title
+        self.selectedDate = defaultDate
+        let h = CGFloat(50 + 250 + kSafeBtm) // UIDatePicker的高度通常为250左右加上底部安全距离
+        super.init(contentHeight: h)
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func setupSubView() {
+        super.setupSubView()
+        contentView.addSubviews([titleLabel, btn, okBtn, datePicker])
+    }
+    
+    override func setupLayout() {
+        super.setupLayout()
+        titleLabel.snp.makeConstraints { (m) in
+            m.top.equalToSuperview()
+            m.left.right.equalToSuperview().inset(60)
+            m.height.equalTo(50)
+        }
+        btn.snp.makeConstraints { (m) in
+            m.centerY.equalTo(titleLabel)
+            m.right.equalToSuperview()
+            m.size.equalTo(60)
+        }
+        okBtn.snp.makeConstraints { make in
+            make.bottom.equalToSuperview().inset(32)
+            make.left.right.equalToSuperview().inset(51)
+            make.height.equalTo(46)
+        }
+        datePicker.snp.makeConstraints { make in
+            make.left.right.equalToSuperview()
+            make.top.equalToSuperview().inset(50)
+            make.bottom.equalTo(okBtn.snp.top).offset(-10)
+        }
+    }
+    
+    override func setupStyle() {
+        super.setupStyle()
+        contentView.backgroundColor = .color(.cellBg)
+        titleLabel.do { (v) in
+            v.textColor = .color(.secondary)
+            v.font = .semiBold(16)
+            v.textAlignment = .center
+            v.text = title
+        }
+        btn.do { v in
+            v.setImage(UIImage(named: "close_icon"), for: .normal)
+            v.addTarget(self, action: #selector(closeAction), for: .touchUpInside)
+        }
+        datePicker.do { v in
+            if #available(iOS 13.4, *) {
+                v.preferredDatePickerStyle = .wheels
+            }
+            v.backgroundColor = .color(.cellBg)
+            v.datePickerMode = .date
+            if DCConfig.appLanguage == .chinse || DCConfig.appLanguage == .Traditional_Chinese {
+                v.locale =  Locale(identifier: "zh_CN")
+            }
+            
+            v.addTarget(self, action: #selector(dateChanged(_:)), for: .valueChanged)
+            v.minimumDate = CustomPicker3.createDate(year: 1900, month: 1, day: 1)
+            v.maximumDate = Date()
+            v.date = selectedDate
+        }
+        okBtn.set(title: "确定", style: .theme1, target: self, action: #selector(okAction))
+    }
+    
+    @objc func dateChanged(_ sender: UIDatePicker) {
+        selectedDate = sender.date
+    }
+    
+    @objc func closeAction() {
+        hide()
+    }
+    
+    @objc func okAction() {
+        didSelected?(selectedDate)
+        hide()
+    }
+    
+    private static func createDate(year: Int, month: Int, day: Int) -> Date {
+        var components = DateComponents()
+        components.year = year
+        components.month = month
+        components.day = day
+        components.hour = 0
+        components.minute = 0
+        components.second = 0
+        return Calendar.current.date(from: components) ?? Date()
+    }
+}
index fa2ab45..51b6b72 100644 (file)
@@ -268,6 +268,331 @@ let chineseMap:[String:String] =
     "嗨!我是":"",
     "查找眼镜":"",
     "眼镜响铃":"",
+    
+    
     "未识别到语音内容":"",
+    
+    "生日":"",
+    "身高":"",
+    "体重":"",
+    "运动偏好":"",
+    "入门":"",
+    "规律运动":"",
+    "资深达人":"",
+    
+    
+    
+    "运动":"",
+    "录音":"",
+    "音乐":"",
+    "户外跑步":"",
+    "户外骑行":"",
+    "健走":"",
+    "运动数据":"",
+    "全部":"",
+    "创建团队":"",
+    "创建我的团队":"",
+    "我创建和已加入的团队":"",
+    "智能运动计划建议":"",
+    "AI规划个人定制运动计划":"",
+    "路线探索":"",
+    "健走/跑步/骑行":"",
+    "目标":"",
+    "路线":"",
+    "组队":"",
+    "语音陪练":"",
+    "积累骑行距离":"",
+    "积累跑步距离":"",
+    "选择播报间距" :"",
+    "设置目标":"",
+    "距离":"",
+    "公里":"",
+    "设置路线":"",
+    "选择规划方式":"",
+    "请选择想要的路线样式":"",
+    "根据距离规划":"",
+    "选择跑步目标":"",
+    "请设置您本次的跑步目标":"",
+    "确认线路":"",
+    "选择一条适合的道路":"",
+    "确定并保存":"",
+    "总距离":"",
+    "时长":"",
+    "配速":"",
+    "卡路里":"",
+    "步数":"",
+    "长按3秒解锁":"",
+    "时速":"",
+    "结束运动":"",
+    "本次运动过短,将不会记录数据,是否结束?":"",
+    "结束":"",
+    "继续运动":"",
+    "平均配速":"",
+    "运动消耗":"",
+    "平均步幅":"",
+    "平均步频":"",
+    "积累爬升":"",
+    "积累下降":"",
+    "最快配速":"",
+    "每公里配速":"",
+    "公里,累计用时":"",
+    "步频":"",
+    "最快步频":"",
+    "最低海拔":"",
+    "最高海拔":"",
+    
+    "运动感受":"",
+    "非常轻松":"",
+    "很轻松":"",
+    "轻松":"",
+    "适中":"",
+    "稍微出汗":"",
+    "困难":"",
+    "很困难":"",
+    "力竭":"",
+    "无法坚持":"",
+    "平均时速":"",
+    "平均速度":"",
+    "最快速度":"",
+    
+    "指导语音密度" :"",
+    "详细":"",
+    "1公里配速、总平均配速、1公里用时、总运动时长":"",
+    "简洁":"",
+    "1公里配速、1公里用时":"",
+    "极简":"",
+    "总距离、总时长":"",
+    "次/分钟":"",
+    "默认":"",
+    "清脆":"",
+    "重击":"",
+
+    
+    "语音播报" :"",
+    "节拍器" :"",
+    "里程播报" :"",
+    "当前已跑里程播报" :"",
+    "训练分段播报" :"",
+    "训练分段开始和结束提示音" :"",
+    "指导播报" :"",
+    "跑步过程中的指导于语音" :"",
+    "节拍器频率" :"",
+    "节拍器声音" :"",
+    "后台保护" :"",
+    "运动权限" :"",
+    "组队设置" :"",
+    "运动实况":"",
+    "组内成员基本运动信息实时显示" :"",
+    "开启灯带,解锁更多运动氛围" :"",
+    "灯带" :"",
+    "常亮" :"",
+    "呼吸" :"",
+    "闪烁":"",
+    "亮度":"",
+    "同步至队员":"",
+    "高亮度提升可见度,低亮度更省电量, 建议根据使用场景合理调节":"",
+    
+    "所有运动":"",
+    "周":"",
+    "月":"",
+    "年":"",
+    "总":"",
+    "总时长":"",
+    "总消耗(千卡)":"",
+    "次数":"",
+    "没有更多内容":"",
+    "运动时长统计":"",
+    "类型":"",
+    "占比":"",
+    "用时":"",
+    "次":"",
+    
+    
+    "设置团队头像":"",
+    "团队名称":"",
+    "给团队起个名字":"",
+    "团队类型":"",
+    "简介":"",
+    "描述一下您的团":"",
+    "灯带设置":"",
+    "加入验证":"",
+    "开启后,成员需要申请才能加入":"",
+    "完成创建":"",
+    "户外跑步团":"",
+    "户外骑行团":"",
+    "户外健走团":"",
+    "团队玩法说明":"",
+    "运动团队是什么?":"",
+    "运动团队是由具备共同运动目标、互补技能的个体组成的集体,成员通过协作配合参与体育项目,追求竞技成绩、团队荣誉或共同健身目标;":"",
+    "团队玩法有什么?":"",
+    "多人在线同步运动时,可实时查看队友的位置与运动数据,随时掌握彼此状态; 在选定时段内,系统会针对不同运动数据指标(如配速、距离等),生成对应的排名; 可自主选择团队内的功能角色,基于角色定位与队友分工协作,提升团队运动效率与配合度。":"",
+    "团队详情":"",
+    "团长:":"",
+    "团队号:":"",
+    "简介:":"",
+    "总人数 (人)":"",
+    "总里程 (公里)":"",
+    "总时长 (分钟)":"",
+    "团队目标":"",
+    "距离 (公里)":"",
+    "时长 (分钟)":"",
+    "卡路里 (千卡)":"",
+    "排行":"",
+    "榜单排名在完成运动后 10~15 分钟更新":"",
+    "进入运动":"",
+    "目标距离":"",
+    "目标时长":"",
+    "目标卡路里":"",
+    "组队运动实况":"",
+    "成绩":"",
+    "配速排序 / 里程排序":"",
+    "选择时间":"",
+    "成立至今":"",
+    "团队信息修改":"",
+    "我的团队角色":"",
+    "成员管理":"",
+    "解散团队":"",
+    "选择你想要承担的团队角色吧":"",
+    "无":"",
+    "破风手":"",
+    "领跑者":"",
+    "跟跑者":"",
+    "冲刺手":"",
+    "配速员":"",
+    "补给员":"",
+    "全能手":"",
+    "团队角色说明":"",
+    "团队角色是什么?":"",
+    "团队运动中各角色是分工明确、协同发力的核心单元,通过精准配合最大化团队运动效果;":"",
+    "破风手:以 “挡风减阻” 为核心,为后方队友节省体能; 领跑者:把控整体节奏,稳定配速避免失衡; 跟跑者:借助风阻优势储备体力,为关键阶段蓄力; 冲刺手:聚焦终点前爆发,锁定最终成绩; 配速员:作为 “节奏标杆”,助力队友校准目标; 补给员:保障物资供应,维持运动续航; 全能手:灵活补位,适配破风、带速等多元需求。":"",
+    "成员列表":"",
+    "邀请":"",
+    "团长转让":"",
+    "移出团队":"",
+    "确认要把团长转让给TA吗?":"",
+    "确认要把TA移出团队吗?":"",
+    "确认要解散当前团队?":"",
+    "解散后所有成员均退出团队,确认解散吗?":"",
+    "确认解散":"",
+    "团队消息":"",
+    "入团申请":"",
+    "申请加入":"",
+    "同意":"",
+    "拒绝":"",
+    "申请通过":"",
+    "申请失败":"",
+    "团队解散":"",
+    "该团队已解散":"",
+    "加入团队":"",
+    "团队号":"",
+    "请输入团队号":"",
+    "打招呼内容":"",
+    "团队角色":"",
+    "退出团队":"",
+    "我的团队":"",
+    "我管理的":"",
+    "我加入的":"",
+    "人已加入":"",
+
+    "生成计划":"",
+    "个性定制,打造最适合你的训练计划":"",
+    "与":"",
+    "的历史对话":"",
+    "当前":"",
+    "你的运动目标是什么":"",
+    "减重":"",
+    "提升跑步能力":"",
+    "你想通过什么运动方式减重":"",
+    "每周运动天数":"",
+    "天":"",
+    "每天运动时长":"",
+    "分钟左右":"",
+    "你每周的跑步距离是多少":"",
+    "你能接受什么难度":"",
+    "零基础":"",
+    "中低强度":"",
+    "中高强度":"",
+    "高强度":"",
+    "你是否存在伤病困扰":"",
+    "不存在伤病困扰":"",
+    "膝盖":"",
+    "关节疼痛、卡顿或不适":"",
+    "腰部":"",
+    "疼痛或下肢麻木":"",
+    "肩部":"",
+    "手腕":"",
+    "查看详情":"",
+    "重新定制":"",
+    "你想提升哪方面的跑步能力":"",
+    "跑步距离提升":"",
+    "跑步速度提升":"",
+    "马拉备赛":"",
+    "步频提升":"",
+    "关于你的跑步能力":"",
+    "当前单次最远跑步距离":"",
+    
+    
+    
+    "我的歌单":"",
+    "我的收藏":"",
+    "全选":"",
+    "完成":"",
+    "您要删除这首歌曲吗?":"",
+    "删除后,这首歌将无法恢复。":"",
+    "定时设置":"",
+    "选择定时":"",
+    "不开启":"",
+    "分钟":"",
+    "倒计时结束后关闭APP":"",
+    "播放完整首后停止":"",
+    "自定义时间":"",
+
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
+    //    "":"",
 ]
 
diff --git a/document/深音APP 骨传导耳机运动功能定义-0122-V1.0.docx b/document/深音APP 骨传导耳机运动功能定义-0122-V1.0.docx
new file mode 100644 (file)
index 0000000..e5c82c8
Binary files /dev/null and "b/document/\346\267\261\351\237\263APP \351\252\250\344\274\240\345\257\274\350\200\263\346\234\272\350\277\220\345\212\250\345\212\237\350\203\275\345\256\232\344\271\211-0122-V1.0.docx" differ