case voiceRecordList(param:[String:Any])
///收藏,或者取消收藏
case voiceRecordCollection(param:[String:Any])
+ /// 获取背景图
+ case backgroundList
}
// 实现TargetType协议
case .voiceRecordCollection(let param):
return "/res/voice-record/favorite/\(param["recordId"]!)?favorite=\(param["favorite"]!)"
-
+ case .backgroundList:
+ return "/dev/devBg/list"
}
}
return nil
}
}
+class BackgroundCache{
+ static func save(url:String){
+ UserDefaults.standard.set(url, forKey: "BackgroundCache")
+ UserDefaults.standard.synchronize()
+ }
+ static func get()->String?{
+ if let value = UserDefaults.standard.value(forKey: "BackgroundCache"),let obj = value as? String {
+ return obj
+ }
+ return nil
+ }
+}
else{
App.shared.goToLogin()
}
+// App.shared.window?.rootViewController = ChatVC()
}
}
appearance.stackedLayoutAppearance.normal.titleTextAttributes = [NSAttributedString.Key.foregroundColor:UIColor.rgb(0x8D8D8D)]
appearance.stackedLayoutAppearance.selected.titleTextAttributes = [NSAttributedString.Key.foregroundColor:UIColor.rgb(0x121212)]
self.tabBar.standardAppearance = appearance
- self.tabBar.backgroundColor = .white
+ self.tabBar.backgroundColor = .clear
view.backgroundColor = .color(.bgColor)
// self.selectedIndex = 2
@objc protocol ChatRecordBarDelegate {
func recordBarStart(_ toolBar:ChatRecordBar)
+ func recordBarCancel(_ toolBar:ChatRecordBar)
func recordBarStop(_ toolBar:ChatRecordBar)
}
class ChatRecordBar:BaseView{
- private var label = UILabel()
- private var animationBg = UIView()
- private var animationView = UIImageView()
+ let recordBtn = RecordButton()
weak var delegate:ChatRecordBarDelegate?
-
- var isRecord:Bool = false{
- didSet{
- if isRecord {
- animationBg.backgroundColor = .hexString("#0092E1")
- animationView.isHidden = false
- label.text = ""
- }
- else{
- animationBg.backgroundColor = .white
- animationView.isHidden = true
- animationView.isHidden = true
- label.text = "按住说话".l10n
- }
- }
- }
-
- override func setupViews() {
- addSubview(animationBg)
- animationBg.addSubviews([label,animationView])
-
- backgroundColor = .clear
- label.do { v in
- v.textColor = .hexString("#121212")
- v.font = .bold(16)
- v.textAlignment = .center
- }
-
- animationBg.do { v in
- v.yc.setCornerRadius(16)
- animationBg.backgroundColor = .white
- }
-
- animationView.kf.setImage(with: Bundle.main.url(forResource: "dialog_record", withExtension: "gif")!)
- self.isRecord = false
- let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
- self.addGestureRecognizer(longPressGesture)
-
- }
- override func setupLayout() {
- animationBg.snp.makeConstraints { make in
- make.left.right.equalToSuperview().inset(18)
+ override func setupViews() {
+// recordBtn.yc.setCornerRadius(16)
+ addSubview(recordBtn)
+ recordBtn.snp.makeConstraints { make in
+ make.left.right.equalToSuperview()
make.top.height.equalToSuperview()
}
- animationView.snp.makeConstraints { make in
- make.width.equalTo(218)
- make.height.equalTo(24)
- make.center.equalToSuperview()
- }
- label.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
- }
-
- @objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
- switch gesture.state {
- case.began:
- Logger.debug("started")
- self.isRecord = true
- delegate?.recordBarStart(self)
- case.ended:
- Logger.debug("ended")
- self.isRecord = false
- delegate?.recordBarStop(self)
- default:
- break
+
+ // 监听状态变化
+ recordBtn.onStatusChanged = { [weak self] status in
+ guard let self = self else { return }
+ switch status {
+ case .start:
+ Logger.debug("开始录音(模拟)")
+ delegate?.recordBarStart(self)
+ case .cancel:
+ Logger.debug("取消录音(模拟)")
+ delegate?.recordBarCancel(self)
+ case .finish:
+ Logger.debug("发送录音(模拟)")
+ delegate?.recordBarStop(self)
+ }
}
}
-
}
guard let self = self else { return }
switch code {
case .startRecord:
- toolBar.isRecord = true
+ toolBar.recordBtn.state = .start
break
case .stopRecord:
- toolBar.isRecord = false
+ toolBar.recordBtn.state = .finish
break
default:
break
}
extension ChatVC:ChatRecordBarDelegate{
+ func recordBarCancel(_ toolBar: ChatRecordBar) {
+ JoyGemChatManager.shared().stopChat()
+ JoyGemChatManager.shared().stopSingleChat()
+ if let item = JoySdkManager.shared.dataList.last,item.content.isEmpty{
+ JoySdkManager.shared.dataList.removeLast()
+ refreshData()
+ }
+ }
+
func recordBarStart(_ toolBar: ChatRecordBar) {
-
let model = ChatCellModel()
model.type = 1
model.content = ""
--- /dev/null
+//
+// Untitled.swift
+// ShenYin
+//
+// Created by liangshi on 2025/11/19.
+//
+
+import UIKit
+import SnapKit
+
+// 对讲状态回调
+enum RecordStatus {
+ case start // 开始(按住)
+ case cancel // 取消(上划)
+ case finish // 完成(松开)
+}
+
+class RecordButton: UIView {
+
+ private var animationBg = UIView()
+ private var animationView = UIImageView()
+
+
+ // 回调闭包
+ var onStatusChanged: ((RecordStatus) -> Void)?
+
+ // 配置属性
+ private let normalColor = UIColor.white
+ private let recordingColor = UIColor.hexString("#0092E1")
+ private let cancelColor = UIColor.red
+ private let titleLabel = UILabel()
+ private let cancelLabel = UILabel()
+
+ // 状态标识
+ private var isRecording = false
+ private var isCanceling = false
+
+ // 取消阈值(可根据需求调整)
+ private let cancelThreshold: CGFloat = 30
+
+ var state:RecordStatus = .start{
+ didSet{
+ if state == .start {
+ animationBg.backgroundColor = .hexString("#0092E1")
+ animationView.isHidden = false
+ titleLabel.isHidden = true
+ cancelLabel.isHidden = true
+ }
+ else{
+ animationBg.backgroundColor = .white
+ animationView.isHidden = true
+ animationView.isHidden = true
+ titleLabel.isHidden = state != .finish
+ cancelLabel.isHidden = true
+ }
+ }
+ }
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ setupUI()
+ }
+
+ required init?(coder: NSCoder) {
+ super.init(coder: coder)
+ setupUI()
+ }
+
+ private func setupUI() {
+ // 基本样式
+// backgroundColor = normalColor
+// layer.cornerRadius = 0 // 圆角在 layoutSubviews 中设置(保证正方形)
+// clipsToBounds = true
+
+
+ titleLabel.do { v in
+ v.textColor = .hexString("#121212")
+ v.font = .bold(16)
+ v.textAlignment = .center
+ titleLabel.text = "按住说话".l10n
+ }
+ cancelLabel.do { v in
+ v.textColor = .hexString("#666666")
+ v.font = .regular(12)
+ v.textAlignment = .center
+ v.text = "松手发送,上移取消".l10n
+ }
+
+ animationBg.do { v in
+ v.yc.setCornerRadius(16)
+ animationBg.backgroundColor = .white
+ }
+
+ animationView.kf.setImage(with: Bundle.main.url(forResource: "dialog_record", withExtension: "gif")!)
+ self.updateUI()
+
+ addSubviews([animationBg,cancelLabel])
+ animationBg.addSubviews([titleLabel,animationView])
+ titleLabel.snp.makeConstraints { make in
+ make.edges.equalToSuperview()
+ }
+ cancelLabel.snp.makeConstraints { make in
+ make.centerX.equalToSuperview()
+ make.top.equalTo(-30)
+ }
+ animationBg.snp.makeConstraints { make in
+ make.left.right.equalToSuperview().inset(18)
+ make.top.height.equalToSuperview()
+ }
+ animationView.snp.makeConstraints { make in
+ make.width.equalTo(218)
+ make.height.equalTo(24)
+ make.center.equalToSuperview()
+ }
+ }
+
+ // 触摸开始(按住)
+ override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
+ super.touchesBegan(touches, with: event)
+ isRecording = true
+ isCanceling = false
+ state = .start
+ updateUI()
+ onStatusChanged?(.start)
+ }
+
+ override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
+ super.touchesMoved(touches, with: event)
+ guard isRecording, let touch = touches.first else { return }
+ let location = touch.location(in: self)
+ isCanceling = location.y < -cancelThreshold
+
+ updateUI()
+ }
+
+ override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
+ super.touchesEnded(touches, with: event)
+ defer {
+ // 重置状态
+ isRecording = false
+ isCanceling = false
+ updateUI()
+ }
+
+ guard isRecording else { return }
+ state = isCanceling ? .cancel : .finish
+ onStatusChanged?(isCanceling ? .cancel : .finish)
+ }
+
+ override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
+ super.touchesCancelled(touches, with: event)
+ isRecording = false
+ isCanceling = false
+ state = .cancel
+ updateUI()
+ onStatusChanged?(.cancel)
+ }
+
+ private func updateUI() {
+ if isRecording {
+ animationView.isHidden = false
+ if isCanceling {
+ animationBg.backgroundColor = .red
+ cancelLabel.isHidden = false
+ titleLabel.isHidden = true
+ }
+ else{
+ cancelLabel.isHidden = true
+ titleLabel.isHidden = true
+ animationBg.backgroundColor = .hexString("#0092E1")
+ }
+ } else {
+ titleLabel.isHidden = false
+ cancelLabel.isHidden = true
+
+ animationBg.backgroundColor = .white
+ animationView.isHidden = true
+ animationView.isHidden = true
+ }
+ }
+
+}
--- /dev/null
+//
+// BackgroundModel.swift
+// ShenYin
+//
+// Created by liangshi on 2025/11/20.
+//
+
+import HandyJSON
+
+class BackgroundModelRoot:ResponseBaseModel {
+ var data = [BackgroundModel]()
+}
+
+class BackgroundModel:HandyJSON {
+ var id = ""
+ var name = ""
+ var bgPath = ""
+ required public init() { }
+}
--- /dev/null
+//
+// BackgroundVC.swift
+// ShenYin
+//
+// Created by liangshi on 2025/11/20.
+//
+
+import UIKit
+import JXSegmentedView
+import MJRefresh
+
+class BackgroundVC: BaseVC{
+ private var collectionView:UICollectionView!
+ var dataList = [BackgroundModel]()
+ var didSelected:((String)->Void)?
+ var currentUrl:String = ""
+
+ override func setupViews() {
+ let layout = UICollectionViewFlowLayout()
+ // layout.minimumInteritemSpacing = 10.fitR
+ layout.minimumLineSpacing = 18
+ layout.itemSize = CGSize(width: (kScreenWid-62)/3, height: 156)
+ collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
+ view.addSubview(collectionView)
+
+ collectionView.do { (v) in
+ v.dataSource = self
+ v.delegate = self
+ v.backgroundColor = .clear
+ v.bounces = true
+ v.contentInset = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18)
+ v.register(BackgroundCell.self, forCellWithReuseIdentifier: BackgroundCell.reuseID)
+ }
+ }
+
+ override func setupLayout() {
+ collectionView.snp.makeConstraints { (m) in
+ m.top.equalToSuperview().inset(kNavBtm)
+ m.left.right.bottom.equalToSuperview()
+ }
+ }
+ override func setupData() {
+ currentUrl = BackgroundCache.get() ?? ""
+ HttpClient.request(.backgroundList) {[weak self] (model:BackgroundModelRoot,res) in
+ guard let self = self else {return}
+ if model.isSuccess{
+ dataList = model.data
+ collectionView.reloadData()
+ }
+ }
+ }
+}
+extension BackgroundVC:UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout {
+ func numberOfSections(in collectionView: UICollectionView) -> Int {
+ return 1
+ }
+ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+ return dataList.count
+ }
+ func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+ let cell = BackgroundCell.cell(withCollectionView: collectionView, indexPath: indexPath)
+ cell.model = dataList[indexPath.row]
+ cell.isCurrent = dataList[indexPath.row].bgPath == currentUrl
+ return cell
+ }
+ func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+ let model = dataList[indexPath.row]
+ didSelected?(model.bgPath)
+ BackgroundCache.save(url: model.bgPath)
+ App.shared.popPage()
+ }
+}
+class BackgroundCell: BaseCollectionViewCell {
+ let bgView = UIView()
+ let imgV = UIImageView()
+ let titleLab = UILabel()
+ var isCurrent:Bool = false{
+ didSet{
+ if isCurrent {
+ imgV.yc.setBorder(width: 3, color: .color(.theme), radius: 12)
+ }
+ else{
+ imgV.yc.setBorder(width: 0, color: .color(.theme), radius: 12)
+ }
+ }
+ }
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ contentView.addSubview(bgView)
+ bgView.addSubview(imgV)
+ bgView.addSubview(titleLab)
+
+// bgView.yc.setCornerRadius(12)
+// bgView.clipsToBounds = true
+// bgView.backgroundColor = .white
+ bgView.snp.makeConstraints { make in
+ make.edges.equalToSuperview()
+ }
+ imgV.snp.makeConstraints { (make) in
+ make.edges.equalToSuperview()
+ }
+
+ titleLab.snp.makeConstraints { (make) in
+ make.left.equalToSuperview().inset(12)
+ make.bottom.equalToSuperview().inset(18)
+ }
+
+ titleLab.textColor = .white
+ titleLab.font = .medium(16)
+
+ imgV.do { v in
+ v.contentMode = .scaleAspectFill
+ v.clipsToBounds = true
+ v.yc.setCornerRadius(12)
+ }
+ }
+
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ var model:BackgroundModel!{
+ didSet{
+// titleLab.text = model.name
+ imgV.kf.setImage(with: model.bgPath.yc.asUrl)
+ }
+ }
+}
class HomeDeviceVC:BaseVC{
private var collectionView:UICollectionView!
let imgView = UIImageView()
+ let edtLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
layout.minimumLineSpacing = 18
layout.itemSize = CGSize(width: (kScreenWid-54)/2, height: 128)
collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
- view.addSubviews([imgView,collectionView])
+ view.addSubviews([imgView,collectionView,edtLabel])
collectionView.do { (v) in
v.dataSource = self
}
imgView.do { v in
// v.backgroundColor = .red
- v.image = UIImage(named: "home_bg")
+ if let url = BackgroundCache.get(){
+ v.kf.setImage(with: url.yc.asUrl)
+ }
+ else{
+ v.image = UIImage(named: "home_bg")
+ }
+ }
+ edtLabel.do { v in
+ v.text = "设置壁纸"
+ v.font = .regular(12)
+ v.backgroundColor = .hexString("#FAFAFA")
+ v.textColor = .hexString("#4E4E4E")
+ v.textAlignment = .center
+ v.yc.setCornerRadius(13)
+ v.yc.addTap(target: self, action: #selector(editAction))
}
}
override func setupLayout() {
imgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
+ edtLabel.snp.makeConstraints { make in
+ make.width.equalTo(72)
+ make.height.equalTo(26)
+ make.centerX.equalToSuperview()
+ make.bottom.equalToSuperview().inset(120)
+ }
}
func reloadData() {
DeviceVM.shared.refresh {[weak self] result in
}
}
override func navRightViewTapped() {
- // App.shared.goToPage(AddDeviceVC())
- // let v = BasePickerView()
- // v.setupData(["2","3","4","5","6","7","8"])
- // v.selectedIndex(4)
- // v.show(animate: true)
-
App.shared.goToPage(BleScanVC())
// App.shared.goToPage(CalendarPickerVC())
-
// BleManager.shared.checkSettingsConnected()
}
-
+ @objc func editAction(){
+ let vc = BackgroundVC()
+ vc.didSelected = {[weak self] url in
+ self?.imgView.kf.setImage(with: url.yc.asUrl)
+ }
+ App.shared.goToPage(vc)
+ }
func unbindAction(_ deviceModel:HomeDeviceModel){
YLprogressHUD.showLoading()
"按日期查找":"按日期查詢",
"绑定安全手机":"綁定安全手機",
"内容由AI生成,请仔细甄别":"內容由AI生成,請仔細甄別",
+ "松手发送,上移取消":"松手發送,上移取消",
+ "设置壁纸":"設定壁紙",
]
"已成功更新到云储存空间":"Đã cập nhật thành công lên kho lưu trữ đám mây",
"按日期查找":"Tìm kiếm theo ngày",
"绑定安全手机":"Li kết nối điện an toàn",
- "内容由AI生成,请仔细甄别":"Nội dung được tạo bởi AI, vui lòng kiểm tra kỹ lưỡng",
+ "内容由AI生成,请仔细甄别":"Nội dung được tạo bởi AI, vui lòng kiểm tra kỹ lưỡng",
+ "松手发送,上移取消":"Thả để gửi, di chuyển lên để hủy",
+ "设置壁纸":"Đặt hình nền",
]
"按日期查找":"",
"绑定安全手机":"",
"内容由AI生成,请仔细甄别":"",
+ "松手发送,上移取消":"",
+ "设置壁纸":"",
]
"已成功更新到云储存空间":"Successfully updated to cloud storage space",
"按日期查找":"Search by Date",
"绑定安全手机":"Bind Secure Mobile Phone",
- "内容由AI生成,请仔细甄别":"Content generated by AI, please verify carefully",
+ "内容由AI生成,请仔细甄别":"Content generated by AI, please verify carefully",
+ "松手发送,上移取消":"Release to send, move up to cancel",
+ "设置壁纸":"Set wallpaper",
]
"已成功更新到云储存空间":"Mise à jour réussie dans l'espace de stockage cloud",
"按日期查找":"Rechercher par date",
"绑定安全手机":"Lier le téléphone sécurisé",
- "内容由AI生成,请仔细甄别":"Contenu généré par IA, veuillez vérifier attentivement",
+ "内容由AI生成,请仔细甄别":"Contenu généré par IA, veuillez vérifier attentivement",
+ "松手发送,上移取消":"Relâchez pour envoyer, déplacez vers le haut pour annuler",
+ "设置壁纸":"Définir l'arrière-plan",
]
"已成功更新到云储存空间":"Berhasil diperbarui ke ruang penyimpanan cloud",
"按日期查找":"Cari berdasarkan tanggal",
"绑定安全手机":"Hubungkan ponsel aman",
- "内容由AI生成,请仔细甄别":"Konten dihasilkan oleh AI, silakan verifikasi dengan saksama",
+ "内容由AI生成,请仔细甄别":"Konten dihasilkan oleh AI, silakan verifikasi dengan saksama",
+ "松手发送,上移取消":"Lepaskan untuk mengirim, pindahkan ke atas untuk membatalkan",
+ "设置壁纸":"Atur wallpaper",
]
"已成功更新到云储存空间":"クラウドストレージスペースに正常に更新されました",
"按日期查找":"日付",
"绑定安全手机":"安全な携帯電話",
- "内容由AI生成,请仔细甄别":"コンテンツはAIによって生成されたものです、詳細に確認してください",
+ "内容由AI生成,请仔细甄别":"コンテンツはAIによって生成されたものです、詳細に確認してください",
+ "松手发送,上移取消":"手を離して送信、上に移動してキャンセル",
+ "设置壁纸":"壁紙を設定",
]
"已成功更新到云储存空间":"클라우드 저장 공간에 성공적으로 업데이트되었습니다",
"按日期查找":"날짜별 검색",
"绑定安全手机":"보안 휴대폰 연결",
- "内容由AI生成,请仔细甄别":"컨텐츠는 AI가 생성한 것이므로, 신중하게 판별해 주세요",
+ "内容由AI生成,请仔细甄别":"컨텐츠는 AI가 생성한 것이므로, 신중하게 판별해 주세요",
+ "松手发送,上移取消":"손을 떼면 전송하고, 위로 이동하면 취소",
+ "设置壁纸":"배경화면 설정",
]
"按日期查找":"Cari mengikut tarikh",
"绑定安全手机":"Hubungkan telefon selamat",
"内容由AI生成,请仔细甄别":"Kandungan dijana oleh AI, sila semak dengan teliti",
+ "松手发送,上移取消":"Lepaskan untuk menghantar, gerakkan ke atas untuk membatalkan",
+ "设置壁纸":"Tetapkan wallpaper",
]
"已成功更新到云储存空间":"Успешно обновлено в облачное хранилище",
"按日期查找":"Поиск по дате",
"绑定安全手机":"Привязать безопасный мобильный телефон",
- "内容由AI生成,请仔细甄别":"Контент сгенерирован ИИ, пожалуйста, тщательно проверяйте",
+ "内容由AI生成,请仔细甄别":"Контент сгенерирован ИИ, пожалуйста, тщательно проверяйте",
+ "松手发送,上移取消":"Отпустите, чтобы отправить, переместите вверх, чтобы отменить",
+ "设置壁纸":"Установить обои",
]
"按日期查找":"Buscar por fecha",
"绑定安全手机":"Vincular teléfono móvil seguro",
"内容由AI生成,请仔细甄别":"Contenido generado por IA, por favor verifique cuidadosamente",
+ "松手发送,上移取消":"Suelte para enviar, mueva hacia arriba para cancelar",
+ "设置壁纸":"Establecer fondo de pantalla",
]
"已成功更新到云储存空间":"อัปเดต到พื้นที่เก็บข้อมูลคลาวด์สำเร็จแล้ว",
"按日期查找":"ค้น โดยตามวันที่",
"绑定安全手机":"เชื่อมโยงโทรศัพท์มือถือปลอดภัย",
- "内容由AI生成,请仔细甄别":"เนื้อหาถูกสร้างโดย AI โปรดตรวจสอบอย่างละเอียด",
+ "内容由AI生成,请仔细甄别":"เนื้อหาถูกสร้างโดย AI โปรดตรวจสอบอย่างละเอียด",
+ "松手发送,上移取消":"ปล่อยเพื่อส่ง ย้ายขึ้นเพื่อยกเลิก",
+ "设置壁纸":"ตั้งภาพพื้นหลัง",
]