case .uploadVoiceFile(_):
return "/res/voice-file/upload"
case .getProductList:
- return "/dev/product/list?name=\(BleManager.shared.connectPeripheral?.device.name ?? "")"
+ return "/dev/product/list?name=\(BleManager.shared.connectPeripheral?.device.name ?? "")&languageCode=\(DCConfig.appLanguage.lanCode)"
case .productBook(let productId):
- return "/res/prod-book-file?productId=\(productId)"
+ return "/res/prod-book-file?productId=\(productId)&languageCode=\(DCConfig.appLanguage.lanCode)"
case .qaCategory:
- return "/uc/qa-category/list"
+ return "/uc/qa-category/list?&languageCode=\(DCConfig.appLanguage.lanCode)"
case .qaList(let categoryId,let page):
- return "/uc/qa/list/\(page)?categoryId=\(categoryId)"
+ return "/uc/qa/list/\(page)?categoryId=\(categoryId)&languageCode=\(DCConfig.appLanguage.lanCode)"
case .commitFeedback(_):
return "/uc/feedback"
break
case let .productInfo(name):
params["prodName"] = name
+ params["languageCode"] = DCConfig.appLanguage.lanCode
break
case let .bindDevice(para):
params += para
--- /dev/null
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "filename" : "Frame 1987@2x.png",
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "filename" : "Frame 1987@3x.png",
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
--- /dev/null
+//
+// ChatCache.swift
+// ShenYin
+//
+// Created by liangshi on 2025/11/10.
+//
+import HandyJSON
+
+/// Ai回应模型
+public class ChatCellModel:NSObject,NSCoding {
+ public var type: Int = 0//1发,0收
+ public var content: String = ""
+ public var id:Int = 0
+
+ required public override init() { }
+
+ public func encode(with aCoder: NSCoder) {
+ aCoder.encode("\(type)", forKey: "type")
+ aCoder.encode(content, forKey: "content")
+ aCoder.encode("\(id)", forKey: "id")
+ }
+
+ required public init?(coder aDecoder: NSCoder) {
+ type = Int(aDecoder.decodeObject(forKey: "type") as! String) ?? 0
+ content = aDecoder.decodeObject(forKey: "content") as? String ?? ""
+ id = Int(aDecoder.decodeObject(forKey: "id") as! String) ?? 0
+ }
+
+}
+
+class ChatCache {
+ static let shared = ChatCache()
+ private let userDefaults = UserDefaults.standard
+
+ func saveList(_ list: [ChatCellModel]) {
+ var fullList = get()
+ fullList.append(contentsOf: list)
+ if fullList.count > 1000{
+ fullList = Array(fullList.dropFirst(fullList.count - 1000))
+ }
+
+ let encoder = NSKeyedArchiver.archivedData(withRootObject: fullList)
+ let filePath = DCFolder.chatCache
+ do {
+ try encoder.write(to: URL(fileURLWithPath: filePath), options:.atomic)
+ } catch {
+ Logger.debug("记录保存失败",filePath,error)
+ }
+ }
+
+ func get() -> [ChatCellModel] {
+ let data = YLToos.readFile(from: DCFolder.chatCache)
+ if data == nil{
+ return []
+ }
+ if let deviceList = NSKeyedUnarchiver.unarchiveObject(with: data!) as? [ChatCellModel] {
+ return deviceList
+ }
+ return []
+ }
+}
--- /dev/null
+//
+// CountryCache.swift
+// ShenYin
+//
+// Created by liangshi on 2025/11/10.
+//
+
+import Foundation
+import HandyJSON
+public class CountryModelRoot: ResponseBaseModel {
+ public var data = [CountryModel]()
+}
+public class CountryModel: NSObject,HandyJSON,NSCoding {
+ public var id = ""
+ public var name = ""
+ public var language = ""
+ public var money = ""
+ public var phonePrefix = ""
+ public var langCode = ""
+
+ var isChinese:Bool{
+ return name == "中国"
+ }
+
+ static func chinese()->CountryModel{
+ let model = CountryModel()
+ model.name = "中国"
+ model.phonePrefix = "86"
+ model.id = "1968848875208851458"
+ model.money = "¥"
+ model.phonePrefix = "+86"
+ model.langCode = "zh-CN"
+
+ return model
+ }
+ required public override init() { }
+
+ public func encode(with aCoder: NSCoder) {
+ aCoder.encode(id, forKey: "id")
+ aCoder.encode(name, forKey: "name")
+ aCoder.encode(language, forKey: "language")
+ aCoder.encode(money, forKey: "money")
+ aCoder.encode(phonePrefix, forKey: "phonePrefix")
+ aCoder.encode(langCode, forKey: "langCode")
+ }
+
+ required public init?(coder aDecoder: NSCoder) {
+ id = aDecoder.decodeObject(forKey: "id") as? String ?? ""
+ name = aDecoder.decodeObject(forKey: "name") as? String ?? ""
+ language = aDecoder.decodeObject(forKey: "language") as? String ?? ""
+ money = aDecoder.decodeObject(forKey: "money") as? String ?? ""
+ phonePrefix = aDecoder.decodeObject(forKey: "phonePrefix") as? String ?? ""
+ langCode = aDecoder.decodeObject(forKey: "langCode") as? String ?? ""
+ }
+}
+
+
+class CountryCache {
+ static let shared = CountryCache()
+ private let userDefaults = UserDefaults.standard
+ private let deviceListKey = "CountryCache"
+
+ func saveList(_ list: [CountryModel]) {
+ let encoder = NSKeyedArchiver.archivedData(withRootObject: list)
+ userDefaults.set(encoder, forKey: deviceListKey)
+ userDefaults.synchronize()
+ }
+
+ func get() -> [CountryModel] {
+ if let data = userDefaults.data(forKey: deviceListKey),
+ let deviceList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [CountryModel] {
+ return deviceList
+ }
+ return []
+ }
+}
}
}
static func recordFileResave(srcPath:String)->String?{
-
let desPath = DCFolder.record + (srcPath as NSString).lastPathComponent
let fileManager = FileManager.default
do {
return nil
}
}
+
+ static var chatCache:String{
+ let path = DCFolder.user + "/chat.txt"
+ if !FileManager.default.fileExists(atPath: path) {
+ FileManager.default.createFile(atPath: path, contents: nil, attributes: nil)
+ }
+ return path
+ }
}
class RecordCache {
static let shared = RecordCache()
private let userDefaults = UserDefaults.standard
- private let deviceListKey = "deviceListKey"
+ private let deviceListKey = "RecordCache"
func add(_ device: RecordDetailModel) {
var deviceList = getList()
//
// Created by liangshi on 2025/8/27.
//
-/// Ai回应模型
-public class ChatCellModel {
- public var type: Int = 0//1发,0收
- public var content: String = ""
- public var id:Int = 0
-}
+
class ChatCell:BaseTableViewCell{
let bgView = LoadingView()
PcmSound.sharedInstance().delegate = self
JoyGemChatManager.shared().addObserver(self, forKeyPath: "isActive",options: [.new, .old], context: nil);
-// let model = ChatCellModel()
-// model.type = 0
-// model.content = "我是你的新朋友,有什么我可以帮助到你的?"
-// model.id = 0
-// dataList.append(model)
+
+// // 创建一个自定义的串行队列
+// let customQueue = DispatchQueue(label: "com.example.customQueue")
+// customQueue.async {
+// // 这里的代码在子线程中执行
+// print("This is running on a background thread. Thread: \(Thread.current)")
+// // 模拟一些耗时操作
+// Thread.sleep(forTimeInterval: 2)
+// DispatchQueue.main.async {
+// // 回到主线程更新UI等操作
+// print("Back on the main thread. Thread: \(Thread.current)")
+// }
+// }
+ YLprogressHUD.showLoading()
+ let list = ChatCache.shared.get()
+ dataList.append(contentsOf: list)
+ refreshData()
+ YLprogressHUD.hidden()
loadRoleList()
}
///结束对话,但并不会终止正在播放的录音
JoyGemChatManager.shared().stopChat()
+
+ ChatCache.shared.saveList(dataList)
+ }
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+ refreshData()
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if (keyPath == "isActive") {
func chatManager(_ manager: JoyGemChatManager, audioData data: Data?, index: UInt, sampleRate: UInt, finish isFinish: Bool) {
if (!isTtsPlaying) {
isTtsPlaying = true
+ Logger.debug("开始播放")
JoyGemChatManager.shared().playAudioStart()
}
+ Logger.debug("收到音频数据", data?.count ?? 0)
PcmSound.sharedInstance().feed(data)
}
--- /dev/null
+//
+// HistorySearchVC.swift
+// ShenYin
+//
+// Created by liangshi on 2025/11/10.
+//
+
class RecordDetaiContent: BaseView {
let titleLabel = UILabel()
- let btn = UIButton(type: .custom)
+ let shareIcon = UIImageView()
+ let copyIcon = UIImageView()
let textView = UITextView()
var title:String! = ""{
didSet{
}
override func setupViews() {
- addSubviews([titleLabel,textView,btn])
+ addSubviews([titleLabel,textView,shareIcon,copyIcon])
titleLabel.do { v in
v.textColor = .rgb(0x0092E1)
v.font = .regular(16)
}
- btn.do { v in
- v.setImage(UIImage(named: "record_detail_copy"), for: .normal)
- v.addTarget(self, action: #selector(copyAction), for: .touchUpInside)
+ shareIcon.do { v in
+// v.backgroundColor = .red
+ v.image = UIImage(named: "share_icon_1")
+ v.yc.addTap(target: self, action: #selector(shareAction))
+ }
+ copyIcon.do { v in
+// v.backgroundColor = .red
+ v.image = UIImage(named: "record_detail_copy")
+ v.yc.addTap(target: self, action: #selector(copyAction))
}
textView.do { v in
v.text = ""
make.top.equalToSuperview().inset(15)
make.left.equalToSuperview().inset(18)
}
- btn.snp.makeConstraints { make in
+ copyIcon.snp.makeConstraints { make in
make.size.equalTo(26)
make.right.equalToSuperview().inset(18)
make.centerY.equalTo(titleLabel)
}
+ shareIcon.snp.makeConstraints { make in
+ make.size.equalTo(26)
+ make.right.equalTo(copyIcon.snp.left).offset(-6)
+ make.centerY.equalTo(titleLabel)
+ }
textView.snp.makeConstraints { make in
make.top.equalToSuperview().inset(35)
make.left.right.equalToSuperview()
}
@objc func copyAction(){
UIPasteboard.general.string = textView.text
-// YLprogressHUD.showAutoHiden("已拷贝")
+ YLprogressHUD.showAutoHiden("已复制")
+ }
+ @objc func shareAction(){
+ if let text = textView.text{
+ let vc = UIActivityViewController(activityItems: [text], applicationActivities: nil)
+ // 对于iPad,设置弹出源
+ // vc.popoverPresentationController?.sourceView = self.view
+ // // 可以排除不需要的分享选项,例如排除打印和复制到剪贴板的选项
+ // vc.excludedActivityTypes = [.print, .copyToPasteboard]
+ vc.modalPresentationStyle = .overFullScreen
+ App.shared.topVC().present(vc, animated: true)
+ return
+ }
}
}
}
switch action {
case .cancel:
- YLprogressHUD.showAutoHiden("请求关闭录音功能/通知关闭了对话")
+ Logger.debug("请求关闭录音功能/通知关闭了对话")
+// YLprogressHUD.showAutoHiden("请求关闭录音功能/通知关闭了对话")
if let topVC = App.shared.topVC(), let vc = topVC as? FieldRecordingVC {
vc.stopRecord()
}
vc.stopRecord()
}
case .recordLive:
- YLprogressHUD.showAutoHiden("请求开启现场录音")
+ Logger.debug("请求开启现场录音")
+// YLprogressHUD.showAutoHiden("请求开启现场录音")
if !App.shared.topVC().isKind(of: FieldRecordingVC.self) {
App.shared.goToPage(FieldRecordingVC(autoStart: true))
}
case .recordCall:
- YLprogressHUD.showAutoHiden("请求开启通话录音")
+ Logger.debug("请求开启通话录音")
+// YLprogressHUD.showAutoHiden("请求开启通话录音")
if !App.shared.topVC().isKind(of: CallingRecordVC.self) {
App.shared.goToPage(CallingRecordVC(autoStart: true))
}
case .si:
- YLprogressHUD.showAutoHiden( "请求开启同传听译")
+ Logger.debug( "请求开启同传听译")
+// YLprogressHUD.showAutoHiden( "请求开启同传听译")
case .chat:
- YLprogressHUD.showAutoHiden( "通知开启了AI对话")
+ Logger.debug("通知开启了AI对话")
+// YLprogressHUD.showAutoHiden( "通知开启了AI对话")
if !App.shared.topVC().isKind(of: ChatVC.self) {
App.shared.goToPage(ChatVC())
}
// Created by liangshi on 2025/8/6.
//
-
import UIKit
-
-class LanguageVC:BaseVC{
+class LanguageVC: BaseVC{
+ let tableView = UITableView(frame: .zero, style: .plain)
+ var currentType = CountryModel()
+ var didSelected:((_ model:CountryModel)->Void)?
override func viewDidLoad() {
super.viewDidLoad()
-
-
+ // Do any additional setup after loading the view.
}
override func setupViews() {
self.navBar.title = "语言设置"
+ tableView.do { v in
+ v.backgroundColor = .rgb(0xF7F7F7)
+ v.delegate = self
+ v.dataSource = self
+ v.rowHeight = 52
+ v.separatorStyle = .none
+ v.register(SelectCell.self, forCellReuseIdentifier: SelectCell.reuseID)
+ }
+ view.addSubviews([tableView])
+
}
override func setupLayout() {
-
+ tableView.snp.makeConstraints { make in
+ make.left.right.bottom.equalToSuperview()
+ make.top.equalTo(kNavBtm)
+ }
+ }
+ override func setupData() {
+ LoginVM.fetchCountryList {[weak self] result in
+ guard let self = self else{return}
+ self.tableView.reloadData()
+ }
+ }
+}
+
+extension LanguageVC:UITableViewDelegate,UITableViewDataSource {
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return LoginVM.shared.countryList.count
}
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let cell = SelectCell.cell(withTableView: tableView, indexPath: indexPath)
+ cell.country = LoginVM.shared.countryList[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: 8, height: 8))
+ }
+ else if indexPath.row == LoginVM.shared.countryList.count - 1{
+ cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.bottomLeft,.bottomRight], cornerRadii: CGSize(width: 8, height: 8))
+ }
+ else{
+ cell.bgView.yc.setCorners(withRoundedRect: rect, roundingCorners: [.allCorners], cornerRadii: CGSize(width: 0, height: 0))
+ }
+ cell.selectedIcon.isHidden = cell.country.id != currentType.id
+ return cell
+ }
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ let data = LoginVM.shared.countryList[indexPath.row]
+ currentType = data
+ tableView.reloadData()
+ didSelected?(data)
+ navigationController?.popViewController(animated: true)
+ }
}
+++ /dev/null
-//
-// CountryModel.swift
-// ShenYin
-//
-// Created by liangshi on 2025/9/27.
-//
-
-import Foundation
-import HandyJSON
-public class CountryModelRoot: ResponseBaseModel {
- public var data = [CountryModel]()
-}
-public class CountryModel: HandyJSON {
- public var id = ""
- public var name = ""
- public var language = ""
- public var money = ""
- public var phonePrefix = ""
-
- required public init() { }
-
- var isChinese:Bool{
- return name == "中国"
- }
-
- static func chinese()->CountryModel{
- let model = CountryModel()
- model.name = "中国"
- model.phonePrefix = "86"
- model.id = "1968848875208851458"
-
- return model
- }
-}
class LoginVM:BaseVM{
static let shared = LoginVM()
var countryList = [CountryModel]()
+
+ override init() {
+ countryList = CountryCache.shared.get()
+
+ }
static public func login(param:[String:Any], completion:@escaping (_ result:LoginModel) ->Void) {
let api = CommomApi.login(param: param)
HttpClient.request(CommomApi.countryList) { (model:CountryModelRoot, res) in
if model.isSuccess{
LoginVM.shared.countryList = model.data
+ CountryCache.shared.saveList(model.data)
}
completion(model)
}
}
-
}