Commit 8bb53f71 authored by Steven杜宇's avatar Steven杜宇

Merge branch 'main' into develop

# Conflicts:
#	galaxy/Podfile
#	galaxy/galaxy.xcodeproj/project.pbxproj
#	galaxy/galaxy/AppDelegate.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/C/YHOrderDetailViewController.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/C/YHServiceCenterViewController.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/M/YHContactItemModel.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/Education&Qualification(学历专业资格填写)/V/YHCollegeSearchBar.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/FamilyMember(家庭成员信息表)/M/YHFamilyInitialInfo.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/OtherInformation(其他信息)/VM/YHOtherInfoFillViewModel.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/PersonInfoList(个人信息预览)/V/YHPreviewInfoNameAndSubNameItemView.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/PersonInfoList(个人信息预览)/V/YHPreviewInfoWorkExpView.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/C/YHWorkExperienceListViewController.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/C/YHWorkIntroductionViewController.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/Model/YHWorkExperienceDetailModel.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/Model/YHWorkExperienceProjectModel.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/V/YHWorkExperiencePositionItemView.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/V/YHWorkExperiencePositionTableViewCell.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/V/YHWorkIntroductionItemView.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/V/YHWorkIntroductionTableViewCell.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/ViewModel/YHWorkExperienceViewModel.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/QMAS(优才)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/ViewModel/YHWorkIntroductionViewModel.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/V/YHWorkIntroductionItemView.swift
#	galaxy/galaxy/Classes/Modules/IntelligentService(服务中心)/ServiceProcess(我的信息流程)/WorkExperience(工作经验)/V/YHWorkIntroductionTableViewCell.swift
#	galaxy/galaxy/Classes/Tools/NetWork/YHAllApiName.swift
#	galaxy/galaxy/Classes/Tools/Upload/YHOSSManager.swift
parents 9795615a 07ee5d51
...@@ -168,7 +168,7 @@ GEM ...@@ -168,7 +168,7 @@ GEM
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-plugin-pgyer (0.2.9) fastlane-plugin-pgyer (0.2.9)
fastlane-plugin-upload_to_bugly (0.1.2) fastlane-plugin-upload_to_bugly (0.1.2)
fastlane-plugin-versioning (0.6.0) fastlane-plugin-versioning (0.7.0)
ffi (1.16.3) ffi (1.16.3)
fourflusher (2.3.1) fourflusher (2.3.1)
fuzzy_match (2.0.4) fuzzy_match (2.0.4)
......
...@@ -6,6 +6,12 @@ platform :ios, '13.0' ...@@ -6,6 +6,12 @@ platform :ios, '13.0'
target 'galaxy' do target 'galaxy' do
use_frameworks! use_frameworks!
#声网SDK播放器等(先弄完整的,后续剪裁)
pod 'AgoraRtcEngine_iOS', '4.4.0' #, :subspecs => ['RtcBasic']
#环信IM 聊天室
pod 'HyphenateChat','4.10.2'
#鸿蒙AGC 短链处理相关 #鸿蒙AGC 短链处理相关
pod 'AGConnectCore','1.9.0.302' pod 'AGConnectCore','1.9.0.302'
pod 'AGConnectAppLinking','1.9.0.302' pod 'AGConnectAppLinking','1.9.0.302'
...@@ -21,7 +27,7 @@ target 'galaxy' do ...@@ -21,7 +27,7 @@ target 'galaxy' do
#网络检查 #网络检查
pod 'ReachabilitySwift','5.0.0' pod 'ReachabilitySwift','5.0.0'
#键盘处理 #键盘处理
pod 'IQKeyboardManagerSwift','7.0.1' pod 'IQKeyboardManagerSwift','8.0.0'
#网络请求 #网络请求
pod 'Alamofire','5.8.1' pod 'Alamofire','5.8.1'
#图片加载 #图片加载
...@@ -73,7 +79,7 @@ target 'galaxy' do ...@@ -73,7 +79,7 @@ target 'galaxy' do
#crash统计 #crash统计
pod 'Bugly','2.6.1' pod 'Bugly','2.6.1'
#神策数据分析 #神策数据分析
pod 'SensorsAnalyticsSDK','4.6.0' pod 'SensorsAnalyticsSDK','4.8.3'
#主流APP分类切换滚动视图 #主流APP分类切换滚动视图
pod 'JXSegmentedView','1.3.0' pod 'JXSegmentedView','1.3.0'
#UI调试组件 #UI调试组件
...@@ -90,7 +96,7 @@ target 'galaxy' do ...@@ -90,7 +96,7 @@ target 'galaxy' do
pod 'SmartCodable','3.4.0' pod 'SmartCodable','3.4.0'
#微信SDK #微信SDK
pod 'WechatOpenSDK', '2.0.2' pod 'WechatOpenSDK', '2.0.4'
#视频播放器 #视频播放器
pod 'BMPlayer', '1.3.0' pod 'BMPlayer', '1.3.0'
#高斯模糊 #高斯模糊
...@@ -104,13 +110,15 @@ target 'galaxy' do ...@@ -104,13 +110,15 @@ target 'galaxy' do
#列表截图 #列表截图
pod 'TYSnapshotScroll', '0.4.0' pod 'TYSnapshotScroll', '0.4.0'
#七鱼SDK #七鱼SDK
pod 'QY_iOS_SDK', '9.9.0' pod 'QY_iOS_SDK', '9.9.2'
#阿里云日志 #阿里云日志
pod 'AliyunLogProducer', '4.3.3' pod 'AliyunLogProducer', '4.3.3'
#加密 #加密
pod 'xxtea', '1.0.2' pod 'xxtea', '1.0.2'
end #Realm
pod 'RealmSwift', '20.0.0'
pod 'Realm', '20.0.0'
end
post_install do |installer| post_install do |installer|
installer.pods_project.targets.each do |target| installer.pods_project.targets.each do |target|
......
...@@ -28,9 +28,15 @@ platform :ios do ...@@ -28,9 +28,15 @@ platform :ios do
main_branch = "main" main_branch = "main"
davidhuang_branch = "davidhuang" davidhuang_branch = "davidhuang"
alex_branch = "main-bugfix-alex" alex_branch = "main-bugfix-alex"
xmas_1130_branch = "qmas-1130"
yinhe_live_1212 = "yinhe-live-1212"
sc_105_branch = "sc-1.0.5"
main_fix = "main-fix"
#打包正使用的分支 #打包正使用的分支
myPack_branch = develop_branch myPack_branch = main_fix
# 打adhoc包 执行命令 fastlane galaxyTest # 打adhoc包 执行命令 fastlane galaxyTest
lane :galaxyTest do lane :galaxyTest do
......
This diff is collapsed.
...@@ -17,16 +17,15 @@ import SensorsAnalyticsSDK ...@@ -17,16 +17,15 @@ import SensorsAnalyticsSDK
import AGConnectCore import AGConnectCore
import AGConnectCredential import AGConnectCredential
import AGConnectAppLinking import AGConnectAppLinking
import HyphenateChat
@main @main
class AppDelegate: UIResponder, UIApplicationDelegate, WXApiDelegate { class AppDelegate: UIResponder, UIApplicationDelegate, WXApiDelegate {
var window: UIWindow? var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Thread.sleep(forTimeInterval: 0.5) // 应产品同学Nick要求 启动页时间展示长点 Thread.sleep(forTimeInterval: 0.5) // 应产品同学Nick要求 启动页时间展示长点
setupAudionConfig() setupAudionConfig()
//初始化 神策sdk //初始化 神策sdk
...@@ -49,6 +48,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, WXApiDelegate { ...@@ -49,6 +48,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, WXApiDelegate {
YHConfigManager.shared.loadConfigData() YHConfigManager.shared.loadConfigData()
YHAnalytics.appInstall()
//5.预加载 //5.预加载
preloadOP() preloadOP()
...@@ -66,10 +66,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate, WXApiDelegate { ...@@ -66,10 +66,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate, WXApiDelegate {
YHOneKeyLoginManager.shared.configOneKeyLogin() YHOneKeyLoginManager.shared.configOneKeyLogin()
YHOSSManager.share.getOSSID() YHOSSManager.share.getOSSID()
AGCInstance.startUp()
AGCAppLinking.instance().handle { (link, error) in
let deepLink = link?.deepLink
//TODO: 增加deeplink地址跳转
}
// QiYu SDK初始化 // QiYu SDK初始化
YHButlerServiceManager.shared.setupSDK() YHButlerServiceManager.shared.setupSDK()
//初始化 环信IM SDK
DispatchQueue.main.async {
let options : EMOptions = EMOptions(appkey: YhConstant.IM.appKey)
options.isAutoLogin = false
EMClient.shared().initializeSDK(with: options)
_ = YHIMHelper.sharedHelper()
}
//设置主窗口 //设置主窗口
setupRootVC() setupRootVC()
return true return true
...@@ -152,16 +167,7 @@ extension AppDelegate { ...@@ -152,16 +167,7 @@ extension AppDelegate {
#else #else
Bugly.start(withAppId: YhConstant.Bugly.appidForRelease) Bugly.start(withAppId: YhConstant.Bugly.appidForRelease)
#endif #endif
//3、AGC 短链相关
AGCInstance.startUp()
AGCAppLinking.instance().handle { (link, error) in
if let deepLink = link?.deepLink {
//TODO: 增加deeplink地址跳转
printLog(deepLink)
}
}
} }
//2.通用全局UI设置相关 //2.通用全局UI设置相关
...@@ -174,10 +180,10 @@ extension AppDelegate { ...@@ -174,10 +180,10 @@ extension AppDelegate {
UITextField.appearance().tintColor = .brandMainColor UITextField.appearance().tintColor = .brandMainColor
UITextView.appearance().tintColor = .brandMainColor UITextView.appearance().tintColor = .brandMainColor
IQKeyboardManager.shared.enable = true IQKeyboardManager.shared.isEnabled = true
IQKeyboardManager.shared.enableAutoToolbar = false IQKeyboardManager.shared.enableAutoToolbar = false
IQKeyboardManager.shared.resignOnTouchOutside = true //控制点击背景是否收起键盘 IQKeyboardManager.shared.resignOnTouchOutside = true //控制点击背景是否收起键盘
IQKeyboardManager.shared.keyboardDistanceFromTextField = 10 // 输入框距离键盘的距离 IQKeyboardManager.shared.keyboardDistance = 10 // 输入框距离键盘的距离
} }
} }
...@@ -203,27 +209,9 @@ extension AppDelegate { ...@@ -203,27 +209,9 @@ extension AppDelegate {
//1.设置HomePageView为主窗口 //1.设置HomePageView为主窗口
func setupRootVCWithTabBar() { func setupRootVCWithTabBar() {
let tabBarController = YHTabBarViewController()
let homeVC = YHHomeHoldViewPageViewController()
tabBarController.delegate = homeVC
let v0 = YHNavigationController(rootVC:homeVC)
let v1 = YHNavigationController(rootVC:YHServiceCenterViewController())
let v2 = YHNavigationController(rootVC:YHCommunityViewController())
let v3 = YHNavigationController(rootVC:YHMyViewController())
v0.tabBarItem = ESTabBarItem.init(YHHomeLottieAnimateContentView(),title: "首页", image: UIImage(named: "home"), selectedImage: UIImage(named: "home_1"))
v1.tabBarItem = ESTabBarItem.init(YHServiceLottieAnimateContentView(),title: "服务", image: UIImage(named: "service"), selectedImage: UIImage(named: "service_1"))
v2.tabBarItem = ESTabBarItem.init(YHCommunityLottieAnimateContentView(),title: "朋友", image: UIImage(named: "msg"), selectedImage: UIImage(named: "msg_1"))
v3.tabBarItem = ESTabBarItem.init(YHMyLottieAnimateContentView(),title: "我的", image: UIImage(named: "me"), selectedImage: UIImage(named: "me_1"))
tabBarController.viewControllers = [v0,v1,v2,v3]
tabBarController.tabBar.backgroundColor = .white
window = UIWindow(frame: UIScreen.main.bounds) window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = tabBarController window?.rootViewController = configTabBarController()
window?.makeKeyAndVisible() window?.makeKeyAndVisible()
if YHLoginManager.shared.needJumpToMsgTabFlag { if YHLoginManager.shared.needJumpToMsgTabFlag {
......
...@@ -26,9 +26,13 @@ class YHBaseViewController: UIViewController { ...@@ -26,9 +26,13 @@ class YHBaseViewController: UIViewController {
#if DEBUG #if DEBUG
IQKeyboardManager.shared.enable = true IQKeyboardManager.shared.isEnabled = true
#endif #endif
// navigationController?.interactivePopGestureRecognizer?.delegate = self // navigationController?.interactivePopGestureRecognizer?.delegate = self
if let key = getKeys(),!key.isEmpty {
YHAnalytics.track(key)
}
} }
override var prefersStatusBarHidden: Bool { override var prefersStatusBarHidden: Bool {
...@@ -45,6 +49,43 @@ class YHBaseViewController: UIViewController { ...@@ -45,6 +49,43 @@ class YHBaseViewController: UIViewController {
#endif #endif
} }
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let key = getKeys(),!key.isEmpty {
YHAnalytics.timeStrat(key)
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let key = getKeys(),!key.isEmpty {
YHAnalytics.timeEnd(key)
}
}
func getKeys() -> String? {
let key = String(describing: type(of: self))
if YHAnalyticsUnit.events.keys.contains(key) {
guard let event = YHAnalyticsUnit.events[key] else { return nil}
if key == "YHHomeInfoDetailContainerViewController" || key == "YHH5WebViewVC" {
let subevent = self.title ?? ""
let events = event as? [String: String] ?? [:]
if events.keys.contains(subevent) {
guard let subEvent = events[subevent] else { return nil}
return subEvent
}
}
if let value = event as? String {
return value
}
}
return nil
}
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
......
...@@ -76,6 +76,7 @@ class YHTabBarViewController: ESTabBarController { ...@@ -76,6 +76,7 @@ class YHTabBarViewController: ESTabBarController {
NotificationCenter.default.addObserver(self, selector: #selector(clearFriendTabBadge), name: YhConstant.YhNotification.didMarkAllMessagesReadedNotifiction, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(clearFriendTabBadge), name: YhConstant.YhNotification.didMarkAllMessagesReadedNotifiction, object: nil)
} }
@objc func getTotalUnreadMsgCount() { @objc func getTotalUnreadMsgCount() {
...@@ -102,8 +103,8 @@ class YHTabBarViewController: ESTabBarController { ...@@ -102,8 +103,8 @@ class YHTabBarViewController: ESTabBarController {
} }
func showFriendTabRedBadgeNumber(_ num: Int) { func showFriendTabRedBadgeNumber(_ num: Int) {
if let vcs = self.viewControllers, vcs.count > 2 { if let vcs = self.viewControllers, vcs.count > 3 {
let friendVC = vcs[2] let friendVC = vcs[3]
if let tabBarItem = friendVC.tabBarItem as? ESTabBarItem { if let tabBarItem = friendVC.tabBarItem as? ESTabBarItem {
// badgeValue 空字符串会显示小红点 nil则隐藏 // badgeValue 空字符串会显示小红点 nil则隐藏
var badge: String? = nil var badge: String? = nil
......
//
// YHAITabBarItem.swift
// galaxy
//
// Created by Dufet on 2024/11/26.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import ESTabBarController_swift
import Lottie
class YHAITabBarItemContentView: ESTabBarItemContentView {
var timer: Timer?
lazy var tipsView: UIView = {
let v = UIView()
let grayView = UIView()
grayView.backgroundColor = UIColor(hex: 0x000000, alpha: 0.6)
grayView.layer.cornerRadius = 6.0
grayView.clipsToBounds = true
v.addSubview(grayView)
let label = UILabel()
label.textColor = UIColor.white
label.textAlignment = .center
label.font = UIFont.PFSC_R(ofSize:12)
label.text = "Hi 我是港小宝,有什么需要帮助的,来找我吧~"
grayView.addSubview(label)
let arrowImgV = UIImageView(image: UIImage(named: "ai_tab_down_arrow"))
v.addSubview(arrowImgV)
arrowImgV.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.width.equalTo(24)
make.height.equalTo(6)
make.bottom.equalTo(-4)
}
label.snp.makeConstraints { make in
make.left.equalTo(15)
make.right.equalTo(-15)
make.height.equalTo(34)
make.top.bottom.equalToSuperview()
}
grayView.snp.makeConstraints { make in
make.left.equalTo(0)
make.right.equalTo(0)
make.top.equalToSuperview()
make.bottom.equalTo(arrowImgV.snp.top)
}
return v
}()
lazy var bottomTitleLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.mainTextColor
label.textAlignment = .center
label.font = UIFont.PFSC_R(ofSize:10)
label.text = "港小宝"
return label
}()
lazy var iconImgView: LottieAnimationView! = {
let lottieView = LottieAnimationView(name: "tab_ai")
lottieView.loopMode = .loop
lottieView.contentMode = .scaleAspectFit
lottieView.play()
return lottieView
}()
override init(frame: CGRect) {
super.init(frame: frame)
createUI()
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func updateLayout() {
super.updateLayout()
}
func createUI() {
self.removeSubviews()
self.addSubview(iconImgView)
self.addSubview(bottomTitleLabel)
self.addSubview(tipsView)
bottomTitleLabel.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.height.equalTo(14)
}
iconImgView.snp.makeConstraints { make in
make.width.height.equalTo(48.0)
make.bottom.equalTo(bottomTitleLabel.snp.top)
make.centerX.equalToSuperview()
}
tipsView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalTo(iconImgView.snp.top)
}
startTimer()
}
func startTimer() {
// 每隔 1 秒调用 timerFired 方法
timer = Timer.scheduledTimer(timeInterval: 5.0,
target: self,
selector: #selector(timerFired),
userInfo: nil,
repeats: false)
}
@objc func timerFired() {
stopTimer()
tipsView.isHidden = true
}
// 停止定时器
func stopTimer() {
timer?.invalidate()
timer = nil
}
deinit {
// 确保在视图控制器被销毁时停止定时器
stopTimer()
}
}
//
// YHAutoTextView.swift
// galaxy
//
// Created by Dufet on 2024/11/20.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHAutoTextView: UITextView, UITextViewDelegate {
var textChange:((String)->())?
override open var text: String! {
didSet {
textViewDidChange(self)
self.setNeedsLayout()
self.layoutIfNeeded()
}
}
let maxHeight = 80.0
static let verticalGap = 5.0
var placeHolder: String = "" {
didSet {
placeholderLabel.text = placeHolder
}
}
let placeholderLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.init(hex: 0xB3C8E9)
label.font = UIFont.PFSC_R(ofSize: 14)
return label
}()
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
self.font = .PFSC_R(ofSize: 14)
delegate = self
isScrollEnabled = false // 禁止滚动
self.addSubview(placeholderLabel)
placeholderLabel.snp.makeConstraints { make in
make.center.equalToSuperview()
make.left.equalTo(5)
make.right.equalTo(-5)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// UITextViewDelegate 方法
func textViewDidChange(_ textView: UITextView) {
// 更新自身的高度
invalidateIntrinsicContentSize()
placeholderLabel.isHidden = !textView.text.isEmpty
textChange?(textView.text)
}
override var intrinsicContentSize: CGSize {
// 计算内容的尺寸
// var height = self.getHeight(text: self.text, font: self.font!, width: frame.width)
// if height < 20.0 {
// height = 20.0
// }
// // 加上textView周边的缝隙
// height += 15.0
let size = sizeThatFits(CGSize(width: frame.width, height: .greatestFiniteMagnitude))
var height = size.height
isScrollEnabled = height > maxHeight
if height > maxHeight {
height = maxHeight
self.snp.updateConstraints { make in
make.top.equalTo(11)
make.bottom.equalTo(-11)
}
} else {
self.snp.updateConstraints { make in
make.top.equalTo(11-Self.verticalGap)
make.bottom.equalTo(-(11-Self.verticalGap))
}
}
return CGSize(width: size.width, height: height)
}
func getHeight(text:String, font:UIFont, width:CGFloat)->CGFloat {
let size = CGSize.init(width:width , height: CGFloat(MAXFLOAT))
let dic = [NSAttributedString.Key.font: font]
let strSize = text.boundingRect(with: size, options: [.usesLineFragmentOrigin], attributes: dic, context:nil).size
return CGSizeMake(strSize.width, ceil(strSize.height)).height
}
}
//
// YHAIChatUIConfiguration.swift
// galaxy
//
// Created by Dufet on 2024/11/29.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHAIChatConfiguration {
var disableHandleMessage: Bool = false
func registerMessageGroupCells(_ tableView: UITableView) {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UITableViewCell")
tableView.register(YHAITextMessageCell.self, forCellReuseIdentifier: YHAITextMessageCell.cellReuseIdentifier)
tableView.register(YHAIThinkingMessageCell.self, forCellReuseIdentifier: YHAIThinkingMessageCell.cellReuseIdentifier)
tableView.register(YHProductListMessageCell.self, forCellReuseIdentifier: YHProductListMessageCell.cellReuseIdentifier)
tableView.register(YHFixProductListMessageCell.self, forCellReuseIdentifier: YHFixProductListMessageCell.cellReuseIdentifier)
tableView.register(YHCardMessageCell.self, forCellReuseIdentifier: YHCardMessageCell.cellReuseIdentifier)
tableView.register(YHAIPictureMessageCell.self, forCellReuseIdentifier: YHAIPictureMessageCell.cellReuseIdentifier)
tableView.register(YHRecommendTextMessageCell.self, forCellReuseIdentifier: YHRecommendTextMessageCell.cellReuseIdentifier)
}
func handleReceiveMessage(_ res: YHAIChatMessage?, _ done: Bool, _ messages: inout [YHAIChatMessage]) {
// 去除loading消息
removeThinkingMessageFromChatList(&messages)
if disableHandleMessage {
for msg in messages {
msg.isDone = true
}
return
}
if let res = res {
var find = false
for msg in messages {
if msg.messageId == res.messageId {
if msg.isNeedSpiceMessage(), res.isNeedSpiceMessage() {
// 多个文字需要拼接
find = true
if res.isTextCompleted() {
msg.isDone = true
} else {
msg.body.contentText += res.body.contentText
}
msg.updateBodyToData()
}
}
}
if !find {
messages.append(res)
}
}
if done { // 思考完成
for msg in messages {
msg.isDone = true
}
}
}
func removeThinkingMessageFromChatList( _ messages: inout [YHAIChatMessage]) {
var findIndex = -1
for (index, msg) in messages.enumerated() {
if msg.getType() == .thinking {
findIndex = index
}
}
if findIndex != -1 {
messages.remove(at: findIndex)
}
}
func createRobotResponseTextMessage(_ text: String) -> YHAIChatMessage {
let message = YHAIChatMessage()
message.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description
message.isSelf = false
let body = YHAIMessageBody()
body.contentType = YHAIMessageType.text.rawValue
body.contentText = text
message.setDone()
message.body = body
message.updateBodyToData()
return message
}
func createRobotResponseLocalPictureMessage(_ imgName: String, previewUrl: String) -> YHAIChatMessage {
let message = YHAIChatMessage()
message.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description
message.isSelf = false
let body = YHAIMessageBody()
body.contentType = YHAIMessageType.picture.rawValue
let imgInfo = YHAIImageInfo()
imgInfo.imageType = YHAIImageType.local.rawValue
imgInfo.localImageName = imgName
imgInfo.imageUrl = previewUrl
body.imageInfo = imgInfo
message.setDone()
message.body = body
message.updateBodyToData()
return message
}
}
//
// YHAIServiceListViewController.swift
// galaxy
//
// Created by Dufet on 2024/11/26.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import JXSegmentedView
import IQKeyboardManagerSwift
let isNeedShowAutoPictureMsg = "isNeedShowAutoPictureMsg"
let isNeedShowAIChatKeyBoard = "isNeedShowAIChatKeyBoard"
class YHAIServiceListViewController: YHBaseViewController {
var serviceArr: [YHEntranceconfigModel] = []
let viewModel = YHAIViewModel()
let manager = YHAIRequestManager()
lazy var collectionView: UICollectionView = {
let flowLayout = UICollectionViewFlowLayout()
flowLayout.minimumInteritemSpacing = 10.0
flowLayout.minimumLineSpacing = 28.0
flowLayout.scrollDirection = .vertical
let collectView = UICollectionView(frame:.zero, collectionViewLayout: flowLayout)
collectView.backgroundColor = .clear
collectView.delegate = self
collectView.dataSource = self
collectView.register(YHAIProductCell.self, forCellWithReuseIdentifier: YHAIProductCell.cellReuseIdentifier)
collectView.register(YHAIGreetCollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: YHAIGreetCollectionReusableView.reuseIdentifier)
collectView.contentInset = .zero
collectView.showsVerticalScrollIndicator = false
return collectView
}()
lazy var bottomInputView: UIView = {
let v = UIView()
let whiteView = UIView()
whiteView.backgroundColor = .white
whiteView.layer.cornerRadius = 12.0
v.addSubview(whiteView)
let label = UILabel()
label.font = .PFSC_R(ofSize: 14)
label.text = "有什么问题尽管问我"
label.textColor = .init(hex: 0xB3C8E9)
whiteView.addSubview(label)
let sendImgV = UIImageView(image: UIImage(named: "ai_chat_send_gray"))
whiteView.addSubview(sendImgV)
let btn = UIButton()
btn.addTarget(self, action: #selector(didInputButtonClicked), for: .touchUpInside)
whiteView.addSubview(btn)
whiteView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
label.snp.makeConstraints { make in
make.left.equalTo(16)
make.height.equalTo(20)
make.centerY.equalToSuperview()
}
sendImgV.snp.makeConstraints { make in
make.right.equalTo(-16)
make.width.height.equalTo(24)
make.centerY.equalToSuperview()
}
btn.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
return v
}()
@objc func didInputButtonClicked() {
let dict:[String : Any] = [isNeedShowAutoPictureMsg : false,
isNeedShowAIChatKeyBoard : true]
NotificationCenter.default.post(name: YhConstant.YhNotification.didSwitchToAIChatNotification, object: dict)
}
override func viewDidLoad() {
super.viewDidLoad()
gk_navBarAlpha = 0
gk_navigationBar.isHidden = true
IQKeyboardManager.shared.isEnabled = false
view.backgroundColor = .clear
view.addSubview(collectionView)
view.addSubview(bottomInputView)
collectionView.snp.makeConstraints { make in
make.left.equalTo(16)
make.right.equalTo(-16)
make.top.equalToSuperview()
make.bottom.equalTo(bottomInputView.snp.top).offset(-8)
}
bottomInputView.snp.makeConstraints { make in
make.left.equalTo(20)
make.right.equalTo(-20)
make.height.equalTo(46)
make.bottom.equalTo(-10-k_Height_safeAreaInsetsBottom())
}
}
}
extension YHAIServiceListViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
// 返回单元格数量
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return serviceArr.count
}
// 返回每个单元格的大小
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = floor((KScreenWidth-16.0*2.0-10.0)/2.0)
let height = width * (200.0/166.0)
return CGSize(width: width, height: height)
}
// 返回自定义单元格
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: YHAIProductCell.cellReuseIdentifier, for: indexPath) as! YHAIProductCell
if 0 <= indexPath.item && indexPath.item < serviceArr.count {
let model = serviceArr[indexPath.item]
cell.model = model
// 根据索引设置旋转
cell.rotateToRight(indexPath.item % 2 == 0)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if 0 <= indexPath.item && indexPath.item < serviceArr.count {
let model = serviceArr[indexPath.item]
if model.redirectMode == 1 { // web页面
if !model.redirectPath.isEmpty {
let vc = YHH5WebViewVC()
vc.url = model.redirectPath
UIViewController.current?.navigationController?.pushViewController(vc, animated: true)
}
} else if model.redirectMode == 2 {
// customerVoice -> APP客户心声 productList -> APP-首页银河甄选 AppServiceTab -> 服务页
if model.redirectPath == YHAIJumpPageType.customerHeart.rawValue {
//客户心声
let vc = YHOtherServiceViewController()
vc.classID = 5
UIViewController.current?.navigationController?.pushViewController(vc)
} else if model.redirectPath == YHAIJumpPageType.galaxySelect.rawValue {
// 银河甄选
let vc = YHSelectViewController()
vc.hideFlag = false
UIViewController.current?.navigationController?.pushViewController(vc, animated: true)
} else if model.redirectPath == YHAIJumpPageType.appServiceTab.rawValue {
// 服务页
UIViewController.current?.navigationController?.popToRootViewController(animated: false)
goTabBarBy(tabType: .service)
}
} else if model.redirectMode == 3 { // agent
if model.businessType == YHAIRobotType.main.rawValue {
// 切到主Robot
let dict:[String : Any] = [isNeedShowAutoPictureMsg : true,
isNeedShowAIChatKeyBoard : false]
NotificationCenter.default.post(name: YhConstant.YhNotification.didSwitchToAIChatNotification, object: dict)
} else {
self.manager.getSubRobotChatConversationId(listItemId: "\(model.id)") {
sesseionId in
let vc = YHAIRobotChatViewController()
vc.isNeedShowBannerHeader = model.isNeedShowBannerHeader()
vc.myTitle = model.title
vc.robotId = model.redirectPath
vc.listItemId = model.id
vc.conversationId = sesseionId
vc.robotType = model.businessType
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
}
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionHeader {
let headerView: YHAIGreetCollectionReusableView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: YHAIGreetCollectionReusableView.reuseIdentifier, for: indexPath) as! YHAIGreetCollectionReusableView
headerView.updateGreetingText()
return headerView
}
return UICollectionReusableView(frame: CGRectMake(0, 0, KScreenWidth, 42.0))
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection:Int) -> CGSize {
return CGSize(width: KScreenWidth, height: 177)
}
}
extension YHAIServiceListViewController: JXSegmentedListContainerViewListDelegate {
func listView() -> UIView {
return view
}
}
//
// YHAITabViewController.swift
// galaxy
//
// Created by Dufet on 2024/11/26.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import JXSegmentedView
import IQKeyboardManagerSwift
class YHAITabViewController: YHBaseViewController {
var defaltIndex: Int = 0
let viewModel = YHAIViewModel()
let arrItemTitles = ["港小宝", "对话"]
var arrItemVCs : [YHBaseViewController] = []
lazy var headerImgView: UIImageView = {
let v = UIImageView(image: UIImage(named: "ai_product_list_head"))
return v
}()
lazy var bgImgView: UIImageView = {
let v = UIImageView(image: UIImage(named: "ai_chat_bg"))
v.isHidden = true
return v
}()
lazy var segmentedView : JXSegmentedView = {
let view = JXSegmentedView(frame: CGRect(x: 0, y: k_Height_safeAreaInsetsTop(), width: KScreenWidth, height: 48))
view.backgroundColor = .clear
return view
}()
lazy var segmentedDataSource: JXSegmentedTitleDataSource = {
let dataSource = JXSegmentedTitleDataSource()
dataSource.isTitleColorGradientEnabled = true
dataSource.titles = arrItemTitles
dataSource.titleNormalFont = UIFont.PFSC_M(ofSize: 16)
dataSource.titleNormalColor = UIColor.init(hex:0x778FB4)
dataSource.titleSelectedFont = UIFont(name: "AlimamaShuHeiTi-Bold", size: 18)
dataSource.titleSelectedColor = .mainTextColor
dataSource.isItemSpacingAverageEnabled = false
dataSource.itemWidth = JXSegmentedViewAutomaticDimension
dataSource.itemSpacing = 28.0
return dataSource
}()
//分类title 所对应的VC
lazy var listContainerView: JXSegmentedListContainerView! = {
let view = JXSegmentedListContainerView(dataSource: self, type: .scrollView)
view.backgroundColor = .clear
return view
}()
lazy var backBtn: UIButton = {
let btn = UIButton()
btn.setImage(UIImage(named: "back_icon"), for: .normal)
btn.YH_clickEdgeInsets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
btn.addTarget(self, action: #selector(didBackBtnClicked), for: .touchUpInside)
return btn
}()
lazy var cleanBtn: UIButton = {
let btn = UIButton()
btn.setImage(UIImage(named: "msg_clean"), for: .normal)
btn.YH_clickEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
btn.addTarget(self, action: #selector(didCleanButtonClicked), for: .touchUpInside)
btn.isHidden = true
return btn
}()
lazy var listVC: YHAIServiceListViewController = {
let vc = YHAIServiceListViewController()
return vc
}()
lazy var mainChatVC: YHAIMainChatViewController = {
let vc = YHAIMainChatViewController()
return vc
}()
@objc func didBackBtnClicked() {
mainChatVC.bottomInputView.showKeyBoard(false)
if segmentedView.selectedIndex == 1 { // AI 对话
// tab切换到港小宝
self.segmentedView.selectItemAt(index: 0)
return
}
self.navigationController?.popViewController(animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .init(hex:0xDEECFE)
gk_navBarAlpha = 0
gk_navigationBar.isHidden = true
createUI()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
requestList()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
func createUI() {
IQKeyboardManager.shared.isEnabled = false
NotificationCenter.default.addObserver(self, selector: #selector(didJumoToAIChat(_:)), name: YhConstant.YhNotification.didSwitchToAIChatNotification, object: nil)
view.addSubview(bgImgView)
view.addSubview(headerImgView)
arrItemVCs.removeAll()
for i in 0..<arrItemTitles.count {
if i == 0 { // 银河AI
arrItemVCs.append(listVC)
} else if i == 1 { // 对话
arrItemVCs.append(mainChatVC)
}
}
//segmentedViewDataSource一定要通过属性强持有!!!!!!!!!
segmentedView.dataSource = segmentedDataSource
view.addSubview(segmentedView)
segmentedView.listContainer = listContainerView
view.addSubview(listContainerView)
view.addSubview(cleanBtn)
//分类Title
segmentedView.dataSource = segmentedDataSource
segmentedView.delegate = self
//配置指示器
let indicator = YHAITabIndicatorView()
indicator.indicatorWidth = 34
indicator.indicatorHeight = 14
indicator.indicatorCornerRadius = 0.0
indicator.verticalOffset = 5
segmentedView.indicators = [indicator]
segmentedView.defaultSelectedIndex = defaltIndex
view.addSubview(backBtn)
backBtn.snp.makeConstraints { make in
make.left.equalTo(16)
make.width.height.equalTo(21)
make.centerY.equalTo(segmentedView)
}
bgImgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
headerImgView.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.height.equalTo(300)
}
let segmentHeight = 48.0
segmentedView.snp.makeConstraints { make in
make.left.equalTo(61-25)
make.right.equalToSuperview()
make.top.equalTo(k_Height_safeAreaInsetsTop())
make.height.equalTo(segmentHeight)
}
listContainerView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalTo(k_Height_safeAreaInsetsTop() + segmentHeight)
make.bottom.equalTo(0)
}
cleanBtn.snp.makeConstraints { make in
make.width.height.equalTo(24)
make.centerY.equalTo(segmentedView)
make.right.equalToSuperview().offset(-20)
}
}
func requestList() {
viewModel.getAIEntranceList {
[weak self] success, error in
guard let self = self else { return }
self.mainChatVC.robotId = self.viewModel.listModel.mainBotId
self.listVC.serviceArr.removeAll()
var arrT = self.viewModel.listModel.entranceConfig
if let configModel = YHConfigManager.shared.reqVM.configModel,configModel.is_integral_open {
// arrT = self.viewModel.listModel.entranceConfig.filter {
// !($0.redirectPath == YHAIJumpPageType.galaxySelect.rawValue)
// }
} else {
//
arrT = self.viewModel.listModel.entranceConfig.filter {
!($0.redirectPath == YHAIJumpPageType.galaxySelect.rawValue)
}
}
self.listVC.serviceArr.append(contentsOf: arrT)
self.listVC.collectionView.reloadData()
}
}
func jumpToItemIndex(itemIndex : Int) {
self.segmentedView.selectItemAt(index: itemIndex)
}
@objc func didJumoToAIChat(_ notify: Notification) {
var showImg = false
var showKeyBoard = false
if let dict = notify.object as? [String : Any] {
if let isShowImg = dict[isNeedShowAutoPictureMsg] as? Bool {
showImg = isShowImg
}
if let isShowKeyBoard = dict[isNeedShowAIChatKeyBoard] as? Bool {
showKeyBoard = isShowKeyBoard
}
}
printLog("SHOW_IMG: \(showImg)")
mainChatVC.isNeedAutoResponseImage = showImg
jumpToItemIndex(itemIndex: 1)
mainChatVC.bottomInputView.showKeyBoard(showKeyBoard)
}
@objc func didCleanButtonClicked() {
self.mainChatVC.didCleanButtonClicked()
}
}
extension YHAITabViewController: JXSegmentedViewDelegate {
func segmentedView(_ segmentedView: JXSegmentedView, didSelectedItemAt index: Int) {
if segmentedView.selectedIndex == 1 { // AI对话
bgImgView.isHidden = false
headerImgView.isHidden = true
cleanBtn.isHidden = false
} else { // 港小宝
bgImgView.isHidden = true
headerImgView.isHidden = false
cleanBtn.isHidden = true
}
}
}
extension YHAITabViewController: JXSegmentedListContainerViewDataSource {
func numberOfLists(in listContainerView: JXSegmentedListContainerView) -> Int {
if let titleDataSource = segmentedView.dataSource as? JXSegmentedBaseDataSource {
return titleDataSource.dataSource.count
}
return 0
}
func listContainerView(_ listContainerView: JXSegmentedListContainerView, initListAt index: Int) -> JXSegmentedListContainerViewListDelegate {
return arrItemVCs[index] as! JXSegmentedListContainerViewListDelegate
}
}
//
// YHAIChatDataBaseManager.swift
// galaxy
//
// Created by Dufet on 2024/12/2.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import Realm
import RealmSwift
class YHAIChatDataBaseManager {
static let shared = YHAIChatDataBaseManager()
let dbName = "AI_CHAT_DB.realm"
// 如果要存储的数据模型属性发生变化,需要配置当前版本号比之前大
let dbVersion: UInt64 = 1
init() {
configDataBase()
}
private func configDataBase() {
let docPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
let dbPath = docPath.appending("/\(dbName)")
let config = Realm.Configuration(fileURL: URL.init(string: dbPath), inMemoryIdentifier: nil, encryptionKey: nil, readOnly: false, schemaVersion: dbVersion, migrationBlock: { (migration, oldSchemaVersion) in
}, deleteRealmIfMigrationNeeded: false, shouldCompactOnLaunch: nil, objectTypes: nil)
Realm.Configuration.defaultConfiguration = config
Realm.asyncOpen { result in
switch result {
case let .success(succeed):
print("Realm 服务器配置成功!")
break
case let .failure(error):
print("Realm 数据库配置失败:\(error.localizedDescription)")
break
}
}
}
private func getDB() -> Realm {
let docPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0] as String
let dbPath = docPath.appending("/\(dbName)")
/// 传入路径会自动创建数据库
let defaultRealm = try! Realm(fileURL: URL.init(string: dbPath)!)
return defaultRealm
}
func addDBMessages(_ messages: [YHAIChatDBMessage]) {
// 获取 Realm 实例
let defaultRealm = self.getDB()
// 开始写入事务
for message in messages {
let students = getDBMessageBy(uuid: message.uuid)
try! defaultRealm.write {
// 将新对象添加到 Realm 数据库
defaultRealm.add(message, update: students.count > 0 ? .modified : .all)
}
}
}
func addDBMessage(_ message: YHAIChatDBMessage) -> Void {
let defaultRealm = self.getDB()
try! defaultRealm.write {
defaultRealm.add(message)
}
print(defaultRealm.configuration.fileURL ?? "")
}
/// 获取 所保存的消息
func getDBMessages() -> Results<YHAIChatDBMessage> {
let defaultRealm = self.getDB()
return defaultRealm.objects(YHAIChatDBMessage.self)
}
func getDBMessageBy(uuid: String) -> Results<YHAIChatDBMessage> {
return self.readDBMessages("uuid = '\(uuid)'")
}
func readDBMessages(_ predicate: String) -> Results<YHAIChatDBMessage> {
let defaultRealm = self.getDB()
print(defaultRealm.configuration.fileURL ?? "")
let predicate = NSPredicate(format: predicate)
let results = defaultRealm.objects(YHAIChatDBMessage.self)
return results.filter(predicate)
}
func readHistoryUIMessages() -> [YHAIChatMessage] {
let result = YHAIChatDataBaseManager.shared.getDBMessages()
var arr = [YHAIChatMessage]()
for msg in result {
let model = msg.getMessage()
arr.append(model)
}
return arr
}
}
//
// YHAIChatDataBaseModel.swift
// galaxy
//
// Created by Dufet on 2024/12/2.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import Realm
import RealmSwift
class YHAIChatDataBaseModel: NSObject {
}
class YHAIChatDBMessage: Object {
@objc var uuid: String = ""
@objc dynamic var id: String = ""
@objc dynamic var event: String = ""
@objc dynamic var data: String = ""
@objc dynamic var is_self: Bool = false
@objc dynamic var is_done: Bool = false
//重写 Object.primaryKey() 可以设置模型的主键。
//声明主键之后,对象将被允许查询,更新速度更加高效,并且要求每个对象保持唯一性。
//一旦带有主键的对象被添加到 Realm 之后,该对象的主键将不可修改。
override static func primaryKey() -> String? {
return "uuid"
}
//重写 Object.ignoredProperties() 可以防止 Realm 存储数据模型的某个属性
override static func ignoredProperties() -> [String] {
// return ["tempID"]
return []
}
//重写 Object.indexedProperties() 方法可以为数据模型中需要添加索引的属性建立索引,Realm 支持为字符串、整型、布尔值以及 Date 属性建立索引。
override static func indexedProperties() -> [String] {
// return ["name"]
return []
}
static func createMessage(_ message: YHAIChatMessage) -> YHAIChatDBMessage {
let msg = YHAIChatDBMessage()
msg.uuid = message.messageId
msg.id = message.id
msg.event = message.event
msg.data = message.data
msg.is_self = message.isSelf
msg.is_done = message.isDone
print("DB WRITE-- \(message)")
return msg
}
func getMessage() -> YHAIChatMessage {
let msg = YHAIChatMessage()
msg.messageId = self.uuid
msg.id = self.id
msg.event = self.event
msg.data = self.data
msg.isSelf = self.is_self
msg.isDone = self.is_done
print("DB READ-- \(msg)")
if let jsonData = self.data.data(using: .utf8) {
do {
let jsonDict = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any]
let dic = (jsonDict ?? [:])
msg.dataDict = dic
if let body = YHAIMessageBody.deserialize(from: dic) {
msg.body = body
}
} catch {
}
}
return msg
}
}
//
// YHAIHistoryMessage.swift
// galaxy
//
// Created by Dufet on 2024/12/3.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import SmartCodable
class YHAIHistoryMessage: SmartCodable {
var botId: String = ""
var chatId: String = ""
var contentText: String = ""
var contentType: Int = 0
var messageId: String = ""
var role: String = ""
var type: String = ""
var cardsInfo: YHAIListInfoModel?
var imageInfo: YHAIImageInfo?
required init() {
}
func convertToChatMessage() -> YHAIChatMessage {
let msg = YHAIChatMessage()
msg.messageId = self.messageId
msg.isSelf = self.role == "user"
msg.isDone = true
let body = YHAIMessageBody()
body.contentType = self.contentType
body.contentText = self.contentText
body.botId = self.botId
body.chatId = self.chatId
body.type = self.type
body.cardsInfo = self.cardsInfo
body.imageInfo = self.imageInfo
msg.body = body
msg.setDone()
msg.updateBodyToData()
return msg
}
}
//
// YHAIListModel.swift
// galaxy
//
// Created by Dufet on 2024/11/27.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import SmartCodable
class YHAIListModel: SmartCodable {
var mainBotId: String = ""
var entranceConfig: [YHEntranceconfigModel] = []
required init() {
}
}
enum YHAIRobotType: String {
case main = "mainBot"
case education = "educationBot"
case sale = "saleBot"
case image = "entryPrecautionsImage"
}
class YHEntranceconfigModel: SmartCodable {
var id: Int = 0
var title: String = ""
var icon: String = ""
var cover: String = ""
var description: String = ""
var btnText: String = ""
var redirectMode: Int = 0
var businessType: String = ""
var redirectPath: String = ""
func isNeedShowBannerHeader() -> Bool {
if businessType == YHAIRobotType.education.rawValue || businessType == YHAIRobotType.sale.rawValue {
return true
}
return false
}
required init() {
}
}
//
// YHAIMessageModel.swift
// galaxy
//
// Created by Dufet on 2024/11/28.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import SmartCodable
class YHAIMessageModel: NSObject {
}
enum YHAIMessageType: Int {
case unknown = 0
case text = 1 // 文字消息
case cardList = 2 // 卡片列表消息
case productList = 3 // 商品列表消息
case picture = 4 // 图片消息
// 自定义case
case thinking = 9527 // 思考消息
case recommendText = -9527 // 推荐文字消息
}
class YHAIChatMessage: CustomStringConvertible, NSCopying {
let completeText = "conversation.chat.completed"
var id: String = ""
var event: String = ""
var data: String = ""
var dataDict: [String : Any] = [:]
var messageId: String = ""
var isSelf: Bool = false
var isDone: Bool = false
var body = YHAIMessageBody()
func copy(with zone: NSZone? = nil) -> Any {
let msg = YHAIChatMessage()
msg.id = self.id
msg.event = self.event
msg.data = self.data
msg.dataDict = self.dataDict
msg.messageId = self.messageId
msg.isSelf = self.isSelf
msg.isDone = self.isDone
msg.body = self.body.copy() as! YHAIMessageBody
return msg
}
func isMessageCompleted() -> Bool {
if self.body.status == "completed" {
return true
}
return false
}
func isTextCompleted() -> Bool {
if self.body.contentType == YHAIMessageType.text.rawValue && self.body.status == "completed" && self.body.type == "answer" {
return true
}
return false
}
func setDone() {
self.isDone = true
}
func isTextMessage() -> Bool {
let type = self.getType()
return type == .text || type == .recommendText
}
func isNeedSpiceMessage() -> Bool {
let type = self.getType()
return type == .text && self.body.type == "answer"
}
static func createQuestionMessage(_ text: String) -> YHAIChatMessage {
let question = YHAIChatMessage()
question.isSelf = true
let body = YHAIMessageBody()
body.contentType = 1
body.contentText = text
question.setDone()
question.body = body
question.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description
question.updateBodyToData()
return question
}
static func createThinkingMesssage() -> YHAIChatMessage {
let message = YHAIChatMessage()
message.isSelf = false
let body = YHAIMessageBody()
body.contentType = YHAIMessageType.thinking.rawValue
message.setDone()
message.body = body
message.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description
message.updateBodyToData()
return message
}
static func createTextMesssage(text: String) -> YHAIChatMessage {
let message = YHAIChatMessage()
message.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description
message.isSelf = false
let body = YHAIMessageBody()
body.contentType = YHAIMessageType.text.rawValue
body.contentText = text
message.setDone()
message.body = body
message.updateBodyToData()
return message
}
static func createPictureMessage(imgType: Int, url: String, downloadUrl: String) -> YHAIChatMessage {
let message = YHAIChatMessage()
message.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description
message.isSelf = false
let body = YHAIMessageBody()
message.setDone()
body.contentType = YHAIMessageType.picture.rawValue
let imgInfo = YHAIImageInfo()
imgInfo.imageType = imgType
imgInfo.imageUrl = url
imgInfo.imageDownloadUrl = downloadUrl
body.imageInfo = imgInfo
message.body = body
message.updateBodyToData()
return message
}
func updateBodyToData() {
dataDict = body.toDictionary() ?? [:]
data = self.dicToJSONString(dict: dataDict)
}
func updateDataToBody() {
}
func dicToJSONString(dict: [String: Any]) -> String {
if let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) {
return String(data: jsonData, encoding: .utf8) ?? ""
}
return ""
}
//
func isUserfulMessage() -> Bool {
if !self.body.isStart() {
return true
}
return false
}
// 获取消息类型
func getType() -> YHAIMessageType {
if let contentType = dataDict["contentType"] as? Int {
if contentType == YHAIMessageType.thinking.rawValue {
return .thinking
}
if contentType == YHAIMessageType.text.rawValue {
if let type = dataDict["type"] as? String {
if type == "follow_up" {
return .recommendText
}
}
return .text
}
if contentType == YHAIMessageType.cardList.rawValue {
return .cardList
}
if contentType == YHAIMessageType.picture.rawValue {
return .picture
}
if contentType == YHAIMessageType.productList.rawValue {
return .productList
}
}
return .unknown
}
// 获取文字消息的文字
func getText() -> String {
let msgType = getType()
if msgType == .text || msgType == .recommendText {
if let text = dataDict["contentText"] as? String, !text.isEmpty {
return text
}
}
return ""
}
var description: String {
return "uudi:\(messageId)\n id:\(id)\n event:\(event)\n data:\(data)\n \n"
}
}
class YHAIMessageBody: SmartCodable, NSCopying {
var contentType: Int = 0
var contentText: String = ""
var contentTextIndex: Int = 0
var chatId: String = ""
var botId: String = ""
var status: String = ""
var type: String = ""
var cardsInfo: YHAIListInfoModel?
var imageInfo: YHAIImageInfo?
func isStart() -> Bool {
if status == "start" {
return true
}
return false
}
func isDone() -> Bool {
if status == "done" {
return true
}
return false
}
func copy(with zone: NSZone? = nil) -> Any {
let body = YHAIMessageBody()
body.contentType = self.contentType
body.contentText = self.contentText
body.chatId = self.chatId
body.botId = self.botId
body.status = self.status
body.type = self.type
return body
}
required init() {
}
// var description: String {
// return "contentType:\(contentType)\n contentText: \(contentText)\n type: \(type)\n status: \(status))\n"
// }
}
enum YHAIJumpPageType: String {
// customerVoice -> APP客户心声 productList -> APP-首页银河甄选 AppServiceTab -> 服务页
case customerHeart = "customerVoice" // APP客户心声
case galaxySelect = "productList" // APP-首页银河甄选
case appServiceTab = "AppServiceTab" // 服务页
}
class YHAIListInfoModel: SmartCodable {
var title: String = ""
var icon: String = ""
var description: String = ""
var btnText: String = ""
var list: [YHAIListItemModel] = []
var redirectMode: Int = 0 // 跳转模式 0: 不跳转 1:web页面跳转 2:APP跳转 3:Agent跳转
// customerVoice -> APP客户心声 productList -> APP-首页银河甄选
var redirectPath: String = ""
var businessType: String = ""
// 是否是测评
func isEvaluation() -> Bool {
return businessType == "evaluation"
}
required init() {
}
}
class YHAIListItemModel: SmartCodable {
var title: String = ""
var cover: String = ""
var description: String = ""
var redirectMode: Int = 0 // 跳转模式 0: 不跳转 1:web页面跳转 2:APP跳转 3:Agent跳转
// customerVoice -> APP客户心声 productList -> APP-首页银河甄选
var redirectPath: String = ""
var tags: [YHProductTag] = []
var originalPrice: String = ""
var discountPrice: String = ""
required init() {
}
}
class YHProductTag: SmartCodable {
var text: String = ""
var color: String = ""
required init() {
}
}
enum YHAIImageType: Int {
case url = 1
case local = 2
}
class YHAIImageInfo: SmartCodable {
var imageType: Int = 0 // 0 url图片 1本地图片
var imageUrl: String = ""
var imageDownloadUrl: String = ""
var localImageName: String = ""
required init() {
}
}
//
// YHAIViewModel.swift
// galaxy
//
// Created by Dufet on 2024/11/27.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHAIViewModel: NSObject {
var listModel: YHAIListModel = YHAIListModel()
var historyMessages: [YHAIHistoryMessage] = []
var lastHistroyMsgId: String = ""
func getAIEntranceList(callBackBlock:@escaping (_ success: Bool,_ error:YHErrorModel?)->()) {
let strUrl = YHBaseUrlManager.shared.curURL() + YHAllApiName.AIChat.aiProductList
let _ = YHNetRequest.getRequest(url: strUrl) { [weak self] json, code in
//1. json字符串 转 对象
guard let self = self else { return }
printLog("model 是 ==> \(json)")
if json.code == 200 {
guard let dic = json.data?.peel as? [String : Any], let resultModel = YHAIListModel.deserialize(from: dic) else {
let err = YHErrorModel(errorCode: YHErrorCode.dictParseError.rawValue, errorMsg: YHErrorCode.dictParseError.description())
callBackBlock(false,err)
return
}
self.listModel = resultModel
callBackBlock(true,nil)
} else {
let err = YHErrorModel(errorCode: Int32(json.code), errorMsg: json.msg.isEmpty ? "" : json.msg)
callBackBlock(false,err)
}
} failBlock: { err in
callBackBlock(false,err)
}
}
func requestConversationId(callBackBlock:@escaping (_ sessionId: String)->()) {
let strUrl = YHBaseUrlManager.shared.curURL() + YHAllApiName.AIChat.getAIChatSessionId
let _ = YHNetRequest.getRequest(url: strUrl) { json, code in
//1. json字符串 转 对象
printLog("model 是 ==> \(json)")
if json.code == 200 {
guard let dic = json.data?.peel as? [String : Any], let sessionId = dic["conversationId"] as? String else {
let err = YHErrorModel(errorCode: YHErrorCode.dictParseError.rawValue, errorMsg: YHErrorCode.dictParseError.description())
callBackBlock("")
return
}
callBackBlock(sessionId)
} else {
let err = YHErrorModel(errorCode: Int32(json.code), errorMsg: json.msg.isEmpty ? "" : json.msg)
callBackBlock("")
}
} failBlock: { err in
callBackBlock("")
}
}
func getHistoryChatMessages(botId: String, conversationId: String, messageId: String, callBackBlock:@escaping (_ success: Bool,_ error:YHErrorModel?)->()) {
let strUrl = YHBaseUrlManager.shared.curURL() + YHAllApiName.AIChat.chatHistory
let params:[String : Any] = [
"botId" : botId,
"conversationId" : conversationId,
"messageId" : messageId
]
let _ = YHNetRequest.getRequest(url: strUrl, params:params) { [weak self] json, code in
//1. json字符串 转 对象
guard let self = self else { return }
printLog("model 是 ==> \(json)")
if json.code == 200 {
guard let dic = json.data?.peel as? [Any], var arr = [YHAIHistoryMessage].deserialize(from: dic) else {
let err = YHErrorModel(errorCode: YHErrorCode.dictParseError.rawValue, errorMsg: YHErrorCode.dictParseError.description())
callBackBlock(false, err)
return
}
if let lastMsg = arr.last {
self.lastHistroyMsgId = lastMsg.messageId
}
// 删除相邻两个相同的iqueston
var result: [YHAIHistoryMessage] = []
for curMsg in arr {
// 仅在结果数组为空或当前字符串与最后一个不同的情况下,才添加当前字符串
if result.isEmpty {
result.append(curMsg)
} else {
if let lastMsg = result.last {
let isSameQuestionMsg = (lastMsg.type == "question" && curMsg.type == "question" && lastMsg.contentText == curMsg.contentText)
if !isSameQuestionMsg {
result.append(curMsg)
}
}
}
}
self.historyMessages = result.reversed()
callBackBlock(true,nil)
} else {
let err = YHErrorModel(errorCode: Int32(json.code), errorMsg: json.msg.isEmpty ? "" : json.msg)
callBackBlock(false,err)
}
} failBlock: { err in
callBackBlock(false,err)
}
}
func createMessage(conversationId: String, role: String, msg: YHAIChatMessage, callBackBlock:@escaping (_ success: Bool,_ error:YHErrorModel?)->()) {
guard let msgDict = msg.body.toDictionary() else {
return
}
let content = self.dicToJSONString(dict: msgDict)
let params:[String : Any] = ["conversationId" : conversationId,
"contentType" : "text",
"role" : role,
"content" : content]
printLog("CREATE_MSG:\n \(params)")
let strUrl = YHBaseUrlManager.shared.curURL() + YHAllApiName.AIChat.createMessage
let _ = YHNetRequest.postRequest(url: strUrl, params: params) {
[weak self] json, code in
//1. json字符串 转 对象
guard let self = self else { return }
printLog("model 是 ==> \(json)")
if json.code == 200 {
callBackBlock(true,nil)
} else {
let err = YHErrorModel(errorCode: Int32(json.code), errorMsg: json.msg.isEmpty ? "" : json.msg)
callBackBlock(false,err)
}
} failBlock: { err in
callBackBlock(false,err)
}
}
func dicToJSONString(dict: [String: Any]) -> String {
if let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) {
return String(data: jsonData, encoding: .utf8) ?? ""
}
return ""
}
}
//
// YHAICardItemView.swift
// galaxy
//
// Created by Dufet on 2024/12/3.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import SDWebImage
class YHAICardItemView: UIView {
var updateBlock:(()->())?
var evaluationResultCallback: ((Dictionary<String, Any>)->())?
var cardModel = YHAIListItemModel() {
didSet {
cardTitleLabel.text = cardModel.title
describeLabel.text = cardModel.description
// 尝试从缓存中获取图片
if let cachedImage = SDImageCache.shared.imageFromCache(forKey: cardModel.cover) {
// 如果缓存中有图片,则直接使用
cardImgView.image = cachedImage
let ratio = self.getSizeRatio(img: cachedImage)
self.cardImgView.snp.remakeConstraints { make in
make.left.equalTo(16)
make.right.equalTo(-16)
make.top.equalTo(self.lineView.snp.bottom).offset(16)
make.height.equalTo(self.cardImgView.snp.width).multipliedBy(ratio)
make.bottom.equalTo(self.cardTitleLabel.snp.top).offset(-10)
}
} else {
cardImgView.sd_setImage(with: URL(string: cardModel.cover), placeholderImage: UIImage(named: "global_default_image"), options: [], completed: { (image, error, cacheType, url) in
self.updateBlock?()
})
}
self.setNeedsLayout()
self.layoutIfNeeded()
}
}
func getSizeRatio(img: UIImage?) -> CGFloat {
let size = img?.size ?? CGSize(width: 100, height: 50)
var ratio = 0.5
if size.width != 0.0, size.height != 0.0 {
ratio = size.height/size.width
}
return ratio
}
lazy var lineView: UIView = {
let v = UIView()
v.backgroundColor = .init(hex: 0xE9ECF0)
return v
}()
lazy var cardImgView: UIImageView = {
let v = UIImageView(image: UIImage(named: "global_default_image"))
v.layer.cornerRadius = 6.0
v.clipsToBounds = true
return v
}()
lazy var cardTitleLabel:UILabel = {
let lable = UILabel()
lable.textColor = UIColor.mainTextColor
lable.textAlignment = .left
lable.font = UIFont.PFSC_B(ofSize:16)
lable.numberOfLines = 0
lable.text = "卡片标题"
return lable
}()
lazy var describeLabel:UILabel = {
let lable = UILabel()
lable.textColor = UIColor.init(hex: 0x8993A2)
lable.textAlignment = .left
lable.font = UIFont.PFSC_R(ofSize:12)
lable.numberOfLines = 0
lable.text = "卡片描述文字"
return lable
}()
override init(frame: CGRect) {
super.init(frame: frame)
createUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createUI() {
let tap = UITapGestureRecognizer(target: self, action: #selector(didItemViewClicked))
self.addGestureRecognizer(tap)
self.addSubview(lineView)
self.addSubview(cardImgView)
self.addSubview(cardTitleLabel)
self.addSubview(describeLabel)
lineView.snp.makeConstraints { make in
make.left.equalTo(16)
make.right.equalTo(-16)
make.top.equalTo(0)
make.height.equalTo(0.5)
}
cardImgView.snp.makeConstraints { make in
make.left.equalTo(16)
make.right.equalTo(-16)
make.top.equalTo(lineView.snp.bottom).offset(16)
make.height.equalTo(cardImgView.snp.width).multipliedBy(0.5)
make.bottom.equalTo(cardTitleLabel.snp.top).offset(-10)
}
cardTitleLabel.snp.makeConstraints { make in
make.left.equalTo(16)
make.right.equalTo(-16)
}
describeLabel.snp.makeConstraints { make in
make.top.equalTo(cardTitleLabel.snp.bottom).offset(4)
make.left.equalTo(16)
make.right.equalTo(-16)
make.bottom.equalTo(-16)
}
}
@objc func didItemViewClicked() {
YHAIJumpPageTool.jumpPageWithType(mode: cardModel.redirectMode, path: cardModel.redirectPath) {
dict in
self.evaluationResultCallback?(dict)
}
}
}
//
// YHAIChatBannerCollectionCell.swift
// galaxy
//
// Created by Dufet on 2024/11/30.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHAIChatBannerCollectionCell: UICollectionViewCell {
static let cellReuseIdentifier = "YHAIChatBannerCollectionCell"
lazy var bgView: UIView = {
let v = UIView()
v.backgroundColor = .white
v.backgroundColor = .white
v.layer.cornerRadius = 6.0
v.clipsToBounds = true
return v
}()
lazy var titleLabel: UILabel = {
var label = UILabel()
label.font = .PFSC_M(ofSize: 12)
label.textAlignment = .left
label.textColor = UIColor(hex: 0x6D788A)
return label
}()
lazy var shadowView: YHAIFlowMessageShadowView = {
let v = YHAIFlowMessageShadowView()
return v
}()
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
private func setupUI() {
contentView.addSubview(shadowView)
contentView.addSubview(bgView)
bgView.addSubview(titleLabel)
bgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
shadowView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
titleLabel.snp.makeConstraints { make in
make.top.bottom.equalToSuperview()
make.left.equalToSuperview().offset(10.0)
make.right.equalToSuperview().offset(-10.0)
}
}
}
//
// YHAIChatBannerItemCell.swift
// galaxy
//
// Created by Dufet on 2024/11/30.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import FSPagerView
import VisualEffectView
class YHAIChatBannerItemCell: FSPagerViewCell {
var model = YHAIChatBannerItem() {
didSet {
titleLabel.text = model.title
subtitleLabel.text = model.desc
}
}
static let cellReuseIdentifier = "YHAIChatBannerItemCell"
lazy var effectView:VisualEffectView = {
let visualEffectView = VisualEffectView()
visualEffectView.colorTint = UIColor(hex: 0xAFAFAF).withAlphaComponent(0.15)
visualEffectView.blurRadius = 16
visualEffectView.scale = 1
visualEffectView.isHidden = true
return visualEffectView
}()
lazy var titleLabel: UILabel = {
let lable = UILabel()
lable.font = UIFont.PFSC_B(ofSize: 14)
lable.textColor = UIColor.brandMainColor
lable.text = "银河明星产品"
return lable
}()
lazy var subtitleLabel: UILabel = {
let lable = UILabel()
lable.font = UIFont.PFSC_R(ofSize: 13)
lable.textColor = UIColor.mainTextColor
lable.text = "这里是副标题"
return lable
}()
override init(frame: CGRect) {
super.init(frame: frame)
initView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func initView() {
contentView.layer.shadowColor = UIColor.clear.cgColor
contentView.layer.shadowRadius = 0
contentView.layer.shadowOpacity = 0
contentView.layer.shadowOffset = .zero
contentView.addSubview(effectView)
effectView.snp.makeConstraints { make in
make.bottom.left.right.equalToSuperview()
make.height.equalTo(95)
}
contentView.addSubview(subtitleLabel)
subtitleLabel.snp.makeConstraints { make in
make.left.equalTo(16)
make.right.equalTo(-16)
make.height.equalTo(20)
make.bottom.equalTo(-35)
}
contentView.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.left.equalTo(16)
make.right.equalTo(-16)
make.height.equalTo(24)
make.bottom.equalTo(subtitleLabel.snp.top)
}
}
}
//
// YHAIChatBannerView.swift
// galaxy
//
// Created by Dufet on 2024/11/30.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import FSPagerView
class YHAIChatBannerItem {
var id: Int = 0
var title: String = ""
var desc: String = ""
var msg: String = ""
required init(id: Int = 0, title: String = "", desc: String = "", msg: String = "") {
self.id = id
self.title = title
self.desc = desc
self.msg = msg
}
}
class YHAIChatBannerView: UIView {
static let bannersHeight = 95.0
let cellHeight: CGFloat = 33.0 // 单元格的固定高度
var selectFlowMsgBlock:((String)->())?
var selectBannerItemBlock:((YHAIChatBannerItem)->())?
var messages:[String] = [] {
didSet {
layout.dataSource = messages
collectionView.reloadData()
}
}
lazy var titleLabel: UILabel = {
let lable = UILabel()
lable.font = UIFont.PFSC_R(ofSize: 12)
lable.textColor = UIColor.init(hex: 0x6D788A)
lable.text = "Hello,我是新港生活规划师"
return lable
}()
lazy var descLabel: UILabel = {
let lable = UILabel()
lable.font = UIFont.PFSC_B(ofSize: 15)
lable.textColor = UIColor.mainTextColor
lable.text = "需要香港身份办理找我~"
return lable
}()
lazy var bgImgV: UIImageView = {
let imagV : UIImageView = UIImageView()
imagV.contentMode = .scaleAspectFill
imagV.clipsToBounds = true
imagV.image = UIImage(named: "ai_chat_header_bg_0")
imagV.isUserInteractionEnabled = true
return imagV
}()
var bannerArr: [YHAIChatBannerItem] = [] {
didSet {
// 设置为0是先停掉自动滑动定时器
bannerView.automaticSlidingInterval = 0
self.indicatorView.indicatorItems = self.bannerArr.count
bannerView.reloadData()
// 指定指示器为第一个
self.indicatorView.curIndicatorIndex = 0
// 指定显示图片为第一个
bannerView.selectItem(at: 0, animated: false)
// 开启定时器开始滚动
bannerView.automaticSlidingInterval = bannerSildingInterval
bannerView.removesInfiniteLoopForSingleItem = true
bannerView.alwaysBounceHorizontal = true
}
}
private lazy var bannerView: FSPagerView = {
let view = FSPagerView()
view.delegate = self
view.dataSource = self
view.bounces = false
view.isInfinite = true
view.automaticSlidingInterval = bannerSildingInterval
view.register(YHAIChatBannerItemCell.self, forCellWithReuseIdentifier: YHAIChatBannerItemCell.cellReuseIdentifier)
view.itemSize = CGSizeMake(KScreenWidth-40.0, YHAIChatBannerView.bannersHeight)//FSPagerView.automaticSize
view.layer.cornerRadius = 4.0
view.clipsToBounds = true
return view
}()
lazy var indicatorView : YHHomeBannerIndicatorView = {
let view = YHHomeBannerIndicatorView()
view.normalColor = .init(hex: 0xD5DAE1)
view.selectedColor = .brandMainColor
view.layer.cornerRadius = 1.0
return view
}()
lazy var layout: YHAIChatCustomFlowLayout = {
let layout = YHAIChatCustomFlowLayout()
layout.scrollDirection = .horizontal
layout.dataSource = []
return layout
}()
lazy var collectionView: UICollectionView = {
let collectView = UICollectionView(frame:.zero, collectionViewLayout: layout)
collectView.delegate = self
collectView.dataSource = self
collectView.backgroundColor = .clear
collectView.showsHorizontalScrollIndicator = false
collectView.showsVerticalScrollIndicator = false
// 注册自定义单元格
collectView.register(YHAIChatBannerCollectionCell.self, forCellWithReuseIdentifier: YHAIChatBannerCollectionCell.cellReuseIdentifier)
return collectView
}()
lazy var shadowView: YHAIChatShadowView = {
let v = YHAIChatShadowView()
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
createUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createUI() {
addSubview(shadowView)
addSubview(bgImgV)
bgImgV.addSubview(titleLabel)
bgImgV.addSubview(descLabel)
bgImgV.addSubview(bannerView)
bannerView.addSubview(indicatorView)
shadowView.snp.makeConstraints { make in
make.left.right.bottom.equalTo(bgImgV)
make.top.equalTo(bgImgV.snp.top).offset(33)
}
bgImgV.snp.makeConstraints { make in
make.top.equalTo(0)
make.left.equalTo(20)
make.right.equalTo(-20)
make.height.equalTo(242)
}
titleLabel.snp.makeConstraints { make in
make.top.equalTo(57)
make.left.equalTo(20)
make.height.equalTo(20)
}
descLabel.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom)
make.left.equalTo(20)
make.height.equalTo(24)
}
bannerView.snp.makeConstraints { make in
make.bottom.equalTo(bgImgV)
make.left.equalTo(0)
make.right.equalTo(0)
make.height.equalTo(YHAIChatBannerView.bannersHeight)
}
indicatorView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.height.equalTo(2)
make.bottom.equalTo(-16)
}
bannerView.reloadData()
addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.left.equalTo(0)
make.right.equalTo(0)
make.height.equalTo(33.0*2+10.0 + 10.0*2.0)
make.top.equalTo(bannerView.snp.bottom).offset(6)
make.bottom.equalTo(-6)
}
}
// 计算文字宽度大小
func getwith(font: UIFont, height: CGFloat, string: String) -> CGSize {
let size = CGSize.init(width: CGFloat(MAXFLOAT), height: height)
let dic = [NSAttributedString.Key.font: font] // swift 4.2
let strSize = string.boundingRect(with: size, options: [.usesLineFragmentOrigin], attributes: dic, context:nil).size
return CGSizeMake(ceil(strSize.width), strSize.height)
}
}
extension YHAIChatBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
public func numberOfItems(in pagerView: FSPagerView) -> Int {
return self.bannerArr.count
}
public func pagerView(_ pagerView: FSPagerView, cellForItemAt index: Int) -> FSPagerViewCell {
let cell = pagerView.dequeueReusableCell(withReuseIdentifier: YHAIChatBannerItemCell.cellReuseIdentifier, at: index) as! YHAIChatBannerItemCell
if index < bannerArr.count {
let model = bannerArr[index]
cell.model = model
}
return cell
}
// MARK: - FSPagerView Delegate
func pagerView(_ pagerView: FSPagerView, didSelectItemAt index: Int) {
pagerView.deselectItem(at: index, animated: true)
pagerView.scrollToItem(at: index, animated: true)
if 0 <= index, index < bannerArr.count {
let model = bannerArr[index]
selectBannerItemBlock?(model)
return
}
}
func pagerViewWillEndDragging(_ pagerView: FSPagerView, targetIndex: Int) {
self.indicatorView.curIndicatorIndex = targetIndex
}
func pagerViewDidEndScrollAnimation(_ pagerView: FSPagerView) {
self.indicatorView.curIndicatorIndex = pagerView.currentIndex
}
}
extension YHAIChatBannerView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
// 返回单元格数量
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
// 返回每个单元格的大小
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return layout.getSize(index: indexPath.item)
}
// 返回自定义单元格
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: YHAIChatBannerCollectionCell.cellReuseIdentifier, for: indexPath) as! YHAIChatBannerCollectionCell
if 0 <= indexPath.item && indexPath.item < messages.count {
cell.titleLabel.text = messages[indexPath.item]
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if 0 <= indexPath.item && indexPath.item < messages.count {
let text = messages[indexPath.item]
selectFlowMsgBlock?(text)
}
}
}
//
// YHAIChatCustomFlowLayout.swift
// galaxy
//
// Created by Dufet on 2024/11/30.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHAIChatCustomFlowLayout: UICollectionViewFlowLayout {
let cellHeight = 33.0
let verticalGap = 10.0
let horizonalGap = 10.0
var firstLineX = 20.0
var secondLineX = 20.0
let firstLineY = 10.0
let secondLineY = 10.0 + 33.0 + 10.0
var attributes: [UICollectionViewLayoutAttributes] = []
// var dataSource:[String] = [] {
// didSet {
// attributes.removeAll()
// firstLineX = 0.0
// secondLineX = 0.0
// for (index, text) in dataSource.enumerated() {
// let attribute = UICollectionViewLayoutAttributes(forCellWith: IndexPath(row: index, section: 0))
// if index % 2 == 0 { // 第一行的数据
// let size = self.getSize(index: index)
// attribute.frame = CGRectMake(firstLineX, verticalGap, size.width, size.height)
// firstLineX = firstLineX + attribute.frame.width + horizonalGap
//
// } else { // 第二行的数据
// let size = self.getSize(index: index)
// attribute.frame = CGRectMake(secondLineX, verticalGap+cellHeight+verticalGap, size.width, size.height)
// secondLineX = secondLineX + attribute.frame.width + horizonalGap
// }
// attributes.append(attribute)
// }
// collectionView?.reloadData()
// }
// }
var dataSource:[String] = [] {
didSet {
attributes.removeAll()
firstLineX = 20.0
secondLineX = 20.0
for (index, text) in dataSource.enumerated() {
let attribute = UICollectionViewLayoutAttributes(forCellWith: IndexPath(row: index, section: 0))
let size = self.getSize(index: index)
if firstLineX <= secondLineX {
attribute.frame = CGRectMake(firstLineX, firstLineY, size.width, size.height)
firstLineX = firstLineX + attribute.frame.width + horizonalGap
} else {
attribute.frame = CGRectMake(secondLineX, secondLineY, size.width, size.height)
secondLineX = secondLineX + attribute.frame.width + horizonalGap
}
attributes.append(attribute)
}
collectionView?.reloadData()
}
}
override func prepare() {
super.prepare()
}
override func invalidateLayout() {
super.invalidateLayout()
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
if 0 <= indexPath.item, indexPath.item < attributes.count {
return attributes[indexPath.item]
}
return nil
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return attributes
}
override var collectionViewContentSize: CGSize {
guard let _ = collectionView else { return .zero }
let width = max(firstLineX, secondLineX)
let size = CGSize(width: width, height: cellHeight*2.0 + verticalGap)
return size
}
func getSize(index:Int) -> CGSize {
if 0 <= index, index < dataSource.count {
let size = self.getwith(font: UIFont.PFSC_M(ofSize: 12), height: cellHeight, string: dataSource[index])
let gap = 10.0
let width = size.width + gap*2.0
return CGSize(width: width, height: cellHeight)
}
return .zero
}
// 计算文字宽度大小
func getwith(font: UIFont, height: CGFloat, string: String) -> CGSize {
let size = CGSize.init(width: CGFloat(MAXFLOAT), height: height)
let dic = [NSAttributedString.Key.font: font] // swift 4.2
let strSize = string.boundingRect(with: size, options: [.usesLineFragmentOrigin], attributes: dic, context:nil).size
return CGSizeMake(ceil(strSize.width), strSize.height)
}
}
//
// YHAIChatInputShadowView.swift
// galaxy
//
// Created by Dufet on 2024/12/2.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import UIKit
class YHAIChatInputShadowView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupShadow()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupShadow()
}
private func setupShadow() {
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 12)
layer.shadowPath = shadowPath.cgPath
layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.12).cgColor
layer.shadowOpacity = 1
layer.shadowRadius = 6
layer.shadowOffset = CGSize(width: 0, height: 0)
}
override func layoutSubviews() {
super.layoutSubviews()
// 确保在布局更新时更新阴影路径
setupShadow()
}
}
//
// YHAIChatShadowView.swift
// galaxy
//
// Created by Dufet on 2024/11/30.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHAIChatShadowView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupShadow()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupShadow()
}
private func setupShadow() {
// 创建阴影路径
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 6)
// 设置阴影属性
layer.shadowPath = shadowPath.cgPath
layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.06).cgColor
layer.shadowOpacity = 1
layer.shadowRadius = 6.0
layer.shadowOffset = CGSize(width: 0, height: 4)
}
override func layoutSubviews() {
super.layoutSubviews()
// 确保在布局更新时更新阴影路径
setupShadow()
}
}
class YHAIFlowMessageShadowView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupShadow()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupShadow()
}
private func setupShadow() {
// 创建阴影路径
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 6)
// 设置阴影属性
layer.shadowPath = shadowPath.cgPath
layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.06).cgColor
layer.shadowOpacity = 1
layer.shadowRadius = 6.0
layer.shadowOffset = CGSize(width: 0, height: 2)
}
override func layoutSubviews() {
super.layoutSubviews()
// 确保在布局更新时更新阴影路径
setupShadow()
}
}
//
// YHAIEvaluationAlertWebView.swift
// galaxy
//
// Created by Dufet on 2024/12/3.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHAIEvaluationAlertWebView: UIView {
var url: String = ""
static func alertView(url: String) -> YHAIEvaluationAlertWebView {
let v = YHAIEvaluationAlertWebView()
return v
}
lazy var blackMaskView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(hex: 0x0F1214, alpha: 0.5)
let tap = UITapGestureRecognizer(target: self, action: #selector(dismiss))
view.addGestureRecognizer(tap)
return view
}()
lazy var bottomView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.addSubview(leftBtn)
view.addSubview(rightBtn)
leftBtn.snp.makeConstraints { make in
make.top.equalTo(8)
make.left.equalTo(16)
make.width.equalTo(rightBtn)
make.right.equalTo(rightBtn.snp.left).offset(-10)
make.height.equalTo(46)
}
rightBtn.snp.makeConstraints { make in
make.top.equalTo(8)
make.right.equalTo(-16)
make.height.equalTo(46)
}
return view
}()
lazy var rightBtn: UIButton = {
let btn = UIButton()
btn.backgroundColor = .brandMainColor
btn.setTitle("重新选择提升目标", for: .normal)
btn.setTitleColor(.white, for: .normal)
btn.titleLabel?.font = UIFont.PFSC_M(ofSize: 16)
btn.addTarget(self, action: #selector(didRightBtnClicked), for: .touchUpInside)
btn.layer.cornerRadius = kCornerRadius3
return btn
}()
lazy var leftBtn: UIButton = {
let btn = UIButton()
btn.backgroundColor = .init(hex: 0xF5F6F8)
btn.setTitle("返回", for: .normal)
btn.setTitleColor(.mainTextColor, for: .normal)
btn.titleLabel?.font = UIFont.PFSC_M(ofSize: 16)
btn.addTarget(self, action: #selector(didLeftBtnClicked), for: .touchUpInside)
btn.layer.cornerRadius = kCornerRadius3
return btn
}()
lazy var webVC: YHH5WebViewVC = {
let vc = YHH5WebViewVC()
vc.url = ""
vc.isNeedRefreshDataFlag = true
vc.isFullScreenFlag = false
return vc
}()
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
createUI()
}
static func alertView() -> YHAppVersionForceUpdateView {
let alertView = YHAppVersionForceUpdateView(frame:UIScreen.main.bounds)
return alertView
}
func createUI() {
self.addSubview(blackMaskView)
self.addSubview(bottomView)
webVC.url = self.url
webVC.view.frame = CGRectMake(0, 100, KScreenWidth, KScreenHeight-100-98)
self.addSubview(webVC.view)
blackMaskView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
bottomView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.height.equalTo(98)
}
webVC.view.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalTo(100)
}
}
@objc func show() {
UIApplication.shared.yhKeyWindow()?.addSubview(self)
}
@objc func dismiss() {
DispatchQueue.main.async {
self.removeFromSuperview()
}
}
@objc func didLeftBtnClicked() {
dismiss()
}
@objc func didRightBtnClicked() {
dismiss()
YHMyNewSchemeViewController.shared.goToSchemePage()
}
}
//
// YHAIEvaluationWebView.swift
// galaxy
//
// Created by Dufet on 2024/12/7.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHAIEvaluationWebView: UIView {
var finishBlock: (([String:Any])->())?
lazy var webVC: YHH5WebViewVC = {
let vc = YHH5WebViewVC()
vc.isFullScreenFlag = true
vc.isHideNavigationBar = true
vc.evaluationResultCallback = {
[weak self] dict in
guard let self = self else { return }
finishBlock?(dict)
self.dismiss()
}
return vc
}()
static func webView(url: String, finishBlock: (([String:Any])->())?) -> YHAIEvaluationWebView {
let view = YHAIEvaluationWebView(frame: UIScreen.main.bounds, url: url)
view.webVC.url = url
view.finishBlock = finishBlock
return view
}
func show() {
self.frame = CGRectMake(0, KScreenHeight, KScreenWidth, KScreenHeight)
UIApplication.shared.yhKeyWindow()?.addSubview(self)
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: {
self.frame = CGRectMake(0, 0, KScreenWidth, KScreenHeight)
}) { finished in
self.frame = CGRectMake(0, 0, KScreenWidth, KScreenHeight)
}
}
func dismiss() {
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: {
self.frame = CGRectMake(0, KScreenHeight, KScreenWidth, KScreenHeight)
}) { finished in
self.removeFromSuperview()
}
}
init(frame: CGRect, url: String) {
super.init(frame: frame)
self.webVC.url = url
createUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createUI() {
self.addSubview(webVC.view)
webVC.view.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
//
// YHAIGreetCollectionReusableView.swift
// galaxy
//
// Created by Dufet on 2024/11/27.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import Lottie
class YHAIGreetCollectionReusableView: UICollectionReusableView {
static let reuseIdentifier: String = "YHAIGreetCollectionReusableView"
let horizonalGap = 16.0
lazy var iconImgView: LottieAnimationView! = {
let lottieView = LottieAnimationView(name: "gangxiaobao_logo")
lottieView.loopMode = .loop
lottieView.contentMode = .scaleAspectFit
lottieView.play()
return lottieView
}()
lazy var quoteImgView: UIImageView = {
let v = UIImageView(image: UIImage(named: "ai_product_list_head_quate"))
return v
}()
lazy var titleLabel:UILabel = {
let lable = UILabel()
lable.textColor = UIColor.mainTextColor
lable.textAlignment = .left
lable.font = UIFont(name: "AlibabaPuHuiTi_3_95_ExtraBold", size: 30)
lable.text = "Hi, 早上好!"
return lable
}()
lazy var subtitleLabel:UILabel = {
let lable = UILabel()
lable.textAlignment = .left
lable.numberOfLines = 0
lable.lineBreakMode = .byCharWrapping
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 4.0
let attributedText = NSAttributedString(
string: "首个聚焦香港的AI智能体,\"港小宝\" 来啦~深度解析身份规划,解锁香港生活未来密码!共赴新港生活奇妙之旅吧!",
attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle,
NSAttributedString.Key.foregroundColor: UIColor.mainTextColor(alpha: 0.6),
NSAttributedString.Key.font: UIFont.PFSC_R(ofSize:14)]
)
lable.attributedText = attributedText
return lable
}()
override init(frame: CGRect) {
super.init(frame: frame)
createUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func updateGreetingText() {
titleLabel.text = greetingBasedOnTime()
}
func createUI() {
self.addSubview(iconImgView)
self.addSubview(titleLabel)
self.addSubview(subtitleLabel)
self.addSubview(quoteImgView)
iconImgView.snp.makeConstraints { make in
make.right.equalTo(-20+horizonalGap)
make.top.equalTo(4)
make.width.equalTo(120)
make.height.equalTo(94)
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(24-horizonalGap)
make.bottom.equalTo(iconImgView.snp.bottom).offset(-15)
make.height.equalTo(42)
}
subtitleLabel.snp.makeConstraints { make in
make.left.equalTo(24-horizonalGap)
make.right.equalTo(-24+horizonalGap)
make.top.equalTo(titleLabel.snp.bottom).offset(8)
}
quoteImgView.snp.makeConstraints { make in
make.width.equalTo(27)
make.height.equalTo(18)
make.right.equalTo(subtitleLabel)
make.bottom.equalTo(subtitleLabel.snp.bottom).offset(10)
}
}
func greetingBasedOnTime() -> String {
let currentDate = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: currentDate)
let minute = calendar.component(.minute, from: currentDate)
var result = "Hi,"
switch (hour, minute) {
case (5..<8, _):
result += "早上好"
case (8, 0..<30):
result += "早上好"
case (8, 30..<60):
result += "上午好"
case (8..<11, _):
result += "上午好"
case (11..<14, _):
result += "中午好"
case (14..<18, _):
result += "下午好"
case (18..<24, _):
result += "晚上好"
case (0..<5, _):
result += "晚上好"
default:
result += "晚上好" // 默认情况
}
result += "!"
return result
}
}
//
// YHAIPictureMessageCell.swift
// galaxy
//
// Created by Dufet on 2024/12/3.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import SDWebImage
class YHAIPictureMessageCell: UITableViewCell {
static let cellReuseIdentifier = "YHAIPictureMessageCell"
var updateBlock: (()->())?
var imgInfo = YHAIImageInfo() {
didSet {
if imgInfo.imageType == YHAIImageType.url.rawValue {
// 尝试从缓存中获取图片
if let cachedImage = SDImageCache.shared.imageFromCache(forKey: imgInfo.imageUrl) {
// 如果缓存中有图片,则直接使用
imgView.image = cachedImage
let size = self.getShowSize(image: cachedImage)
self.imgView.snp.updateConstraints { make in
make.width.equalTo(size.width)
make.height.equalTo(size.height)
}
} else {
imgView.sd_setImage(with: URL(string: imgInfo.imageUrl), placeholderImage: UIImage(named: "global_default_image"), options: [], completed: { (image, error, cacheType, url) in
self.updateBlock?()
})
}
} else if imgInfo.imageType == YHAIImageType.local.rawValue {
if let img = UIImage(named: imgInfo.localImageName) {
imgView.image = img
self.imgView.snp.updateConstraints { make in
make.width.equalTo(img.size.width)
make.height.equalTo(img.size.height)
}
} else {
imgView.image = UIImage(named: "global_default_image")
self.imgView.snp.updateConstraints { make in
make.width.equalTo(200)
make.height.equalTo(200)
}
}
}
self.setNeedsLayout()
self.layoutIfNeeded()
}
}
func getShowSize(image: UIImage?) -> CGSize {
let imgW = 220.0
var imgH = 220.0
var ratio = 1.0
guard let img = image else {
return CGSizeMake(imgW, imgH)
}
if img.size.width > 0, img.size.height > 0 {
ratio = img.size.width/img.size.height
}
imgH = imgW/ratio
// if imgH > 476 {
// imgH = 476
// imgW = imgH*ratio
//
// } else {
// imgW = 220
// imgH = imgW/ratio
// }
return CGSizeMake(imgW, imgH)
}
lazy var imgView: UIImageView = {
let v = UIImageView(image: UIImage(named: "global_default_image"))
return v
}()
lazy var whiteContentView: UIView = {
let v = UIView()
v.layer.cornerRadius = 12.0
v.clipsToBounds = true
let tap = UITapGestureRecognizer(target: self, action: #selector(didMessageClicked))
v.addGestureRecognizer(tap)
return v
}()
lazy var shadowView: YHAIChatShadowView = {
let v = YHAIChatShadowView()
return v
}()
lazy var rightAngleView: UIView = {
let v = UIView()
v.backgroundColor = .white
v.isHidden = true
return v
}()
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
func setupUI() {
selectionStyle = .none
contentView.backgroundColor = .clear
backgroundColor = .clear
contentView.addSubview(shadowView)
contentView.addSubview(rightAngleView)
contentView.addSubview(whiteContentView)
whiteContentView.addSubview(imgView)
whiteContentView.snp.makeConstraints { make in
make.left.equalTo(20)
make.top.equalTo(20)
make.bottom.equalTo(0)
}
imgView.snp.makeConstraints { make in
make.width.equalTo(220)
make.height.equalTo(220)
make.edges.equalToSuperview()
}
shadowView.snp.makeConstraints { make in
make.edges.equalTo(whiteContentView)
}
rightAngleView.snp.makeConstraints { make in
make.top.left.equalTo(whiteContentView)
make.width.height.equalTo(15)
}
}
@objc func didMessageClicked() {
self.endEditing(true)
YHPictureReviewManager.shared.showNetWorkPicturs(curIndex: 0, arrPicturs: [imgInfo.imageUrl])
}
}
//
// YHAIProductCell.swift
// galaxy
//
// Created by Dufet on 2024/11/27.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHAIProductCell: UICollectionViewCell {
static let cellReuseIdentifier = "YHAIProductCell"
var model = YHEntranceconfigModel() {
didSet {
titleLabel.text = model.title
subtitleLabel.text = model.description
if !model.btnText.isEmpty {
var text = model.btnText
if text.count > 5 {
text = String(text.prefix(5))
}
blackTitleLabel.text = text
} else {
blackTitleLabel.text = "去了解"
}
bgImgView.sd_setImage(with: URL(string: model.cover), placeholderImage: UIImage(named: "ai_product_bg_default"))
}
}
lazy var bgImgView: UIImageView = {
let v = UIImageView(image: UIImage(named: "ai_product_bg_default"))
return v
}()
lazy var titleLabel:UILabel = {
let lable = UILabel()
lable.textColor = UIColor.mainTextColor
lable.textAlignment = .left
lable.font = UIFont.PFSC_B(ofSize:15)
lable.text = "产品智能体"
return lable
}()
lazy var subtitleLabel:UILabel = {
let lable = UILabel()
lable.textColor = UIColor.init(hex:0x8993A2)
lable.textAlignment = .left
lable.font = UIFont.PFSC_R(ofSize:12)
lable.text = "银河在售商品介绍"
return lable
}()
lazy var blackView: UIView = {
let v = UIView()
v.backgroundColor = .black
v.layer.cornerRadius = 13.0
v.clipsToBounds = true
v.layer.allowsEdgeAntialiasing = true
v.addSubview(blackTitleLabel)
blackTitleLabel.snp.makeConstraints { make in
make.left.equalTo(10)
make.right.equalTo(-10)
make.top.bottom.equalToSuperview()
}
return v
}()
lazy var blackTitleLabel: UILabel = {
let lable = UILabel()
lable.textColor = UIColor.white
lable.textAlignment = .left
lable.font = UIFont.PFSC_M(ofSize: 11)
lable.text = "去了解"
return lable
}()
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
func setupUI() {
contentView.backgroundColor = .clear
backgroundColor = .clear
contentView.addSubview(bgImgView)
bgImgView.addSubview(titleLabel)
bgImgView.addSubview(subtitleLabel)
bgImgView.addSubview(blackView)
bgImgView.snp.makeConstraints { make in
make.left.equalTo(0)
make.right.equalTo(0)
make.bottom.equalTo(0)
make.top.equalTo(0)
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(22)
make.right.equalTo(-22)
make.bottom.equalTo(subtitleLabel.snp.top).offset(-3)
make.height.equalTo(20)
}
subtitleLabel.snp.makeConstraints { make in
make.left.equalTo(22)
make.right.equalTo(-22)
make.bottom.equalTo(blackView.snp.top).offset(-12)
}
blackView.snp.makeConstraints { make in
make.left.equalTo(22)
make.bottom.equalTo(-24)
make.height.equalTo(26)
make.right.lessThanOrEqualTo(-22)
}
}
func rotateToRight(_ isRotateRight: Bool) {
let degree = isRotateRight ? 2.0 : -2.0
let transform = CGAffineTransform(rotationAngle: CGFloat(degree * CGFloat.pi / 180)) // 2 度转换为弧度
bgImgView.transform = transform
}
}
//
// YHAITabIndicatorView.swift
// galaxy
//
// Created by Dufet on 2024/11/28.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import JXSegmentedView
class YHAITabIndicatorView: JXSegmentedIndicatorBaseView {
lazy var bgImgView: UIImageView = {
let v = UIImageView(image: UIImage(named: "ai_tab_indicator"))
return v
}()
open var lineStyle: JXSegmentedIndicatorLineStyle = .normal
/// lineStyle为lengthenOffset时使用,滚动时x的偏移量
open var lineScrollOffsetX: CGFloat = 10
open override func commonInit() {
super.commonInit()
self.backgroundColor = .clear
self.addSubview(bgImgView)
bgImgView.snp.makeConstraints { make in
make.width.equalTo(34)
make.height.equalTo(14)
make.centerX.equalToSuperview()
make.top.equalTo(0)
}
}
open override func refreshIndicatorState(model: JXSegmentedIndicatorSelectedParams) {
super.refreshIndicatorState(model: model)
backgroundColor = .clear
layer.cornerRadius = getIndicatorCornerRadius(itemFrame: model.currentSelectedItemFrame)
let width = getIndicatorWidth(itemFrame: model.currentSelectedItemFrame, itemContentWidth: model.currentItemContentWidth)
let height = getIndicatorHeight(itemFrame: model.currentSelectedItemFrame)
let x = model.currentSelectedItemFrame.origin.x + (model.currentSelectedItemFrame.size.width - width)/2
var y: CGFloat = 0
switch indicatorPosition {
case .top:
y = verticalOffset
case .bottom:
y = model.currentSelectedItemFrame.size.height - height - verticalOffset
case .center:
y = (model.currentSelectedItemFrame.size.height - height)/2 + verticalOffset
}
frame = CGRect(x: x, y: y, width: width, height: height)
}
open override func contentScrollViewDidScroll(model: JXSegmentedIndicatorTransitionParams) {
super.contentScrollViewDidScroll(model: model)
guard canHandleTransition(model: model) else {
return
}
let rightItemFrame = model.rightItemFrame
let leftItemFrame = model.leftItemFrame
let percent = model.percent
var targetX: CGFloat = leftItemFrame.origin.x
var targetWidth = getIndicatorWidth(itemFrame: leftItemFrame, itemContentWidth: model.leftItemContentWidth)
let leftWidth = targetWidth
let rightWidth = getIndicatorWidth(itemFrame: rightItemFrame, itemContentWidth: model.rightItemContentWidth)
let leftX = leftItemFrame.origin.x + (leftItemFrame.size.width - leftWidth)/2
let rightX = rightItemFrame.origin.x + (rightItemFrame.size.width - rightWidth)/2
switch lineStyle {
case .normal:
targetX = JXSegmentedViewTool.interpolate(from: leftX, to: rightX, percent: CGFloat(percent))
if indicatorWidth == JXSegmentedViewAutomaticDimension {
targetWidth = JXSegmentedViewTool.interpolate(from: leftWidth, to: rightWidth, percent: CGFloat(percent))
}
case .lengthen:
//前50%,只增加width;后50%,移动x并减小width
let maxWidth = rightX - leftX + rightWidth
if percent <= 0.5 {
targetX = leftX
targetWidth = JXSegmentedViewTool.interpolate(from: leftWidth, to: maxWidth, percent: CGFloat(percent*2))
}else {
targetX = JXSegmentedViewTool.interpolate(from: leftX, to: rightX, percent: CGFloat((percent - 0.5)*2))
targetWidth = JXSegmentedViewTool.interpolate(from: maxWidth, to: rightWidth, percent: CGFloat((percent - 0.5)*2))
}
case .lengthenOffset:
//前50%,增加width,并少量移动x;后50%,少量移动x并减小width
let maxWidth = rightX - leftX + rightWidth - lineScrollOffsetX*2
if percent <= 0.5 {
targetX = JXSegmentedViewTool.interpolate(from: leftX, to: leftX + lineScrollOffsetX, percent: CGFloat(percent*2))
targetWidth = JXSegmentedViewTool.interpolate(from: leftWidth, to: maxWidth, percent: CGFloat(percent*2))
}else {
targetX = JXSegmentedViewTool.interpolate(from:leftX + lineScrollOffsetX, to: rightX, percent: CGFloat((percent - 0.5)*2))
targetWidth = JXSegmentedViewTool.interpolate(from: maxWidth, to: rightWidth, percent: CGFloat((percent - 0.5)*2))
}
}
self.frame.origin.x = targetX
self.frame.size.width = targetWidth
}
open override func selectItem(model: JXSegmentedIndicatorSelectedParams) {
super.selectItem(model: model)
let targetWidth = getIndicatorWidth(itemFrame: model.currentSelectedItemFrame, itemContentWidth: model.currentItemContentWidth)
var toFrame = self.frame
toFrame.origin.x = model.currentSelectedItemFrame.origin.x + (model.currentSelectedItemFrame.size.width - targetWidth)/2
toFrame.size.width = targetWidth
if canSelectedWithAnimation(model: model) {
UIView.animate(withDuration: scrollAnimationDuration, delay: 0, options: .curveEaseOut, animations: {
self.frame = toFrame
}) { (_) in
}
}else {
frame = toFrame
}
}
}
//
// YHAIThinkingMessageCell.swift
// galaxy
//
// Created by Dufet on 2024/11/28.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
import Lottie
class YHAIThinkingMessageCell: UITableViewCell {
static let cellReuseIdentifier = "YHAIThinkingMessageCell"
var whiteContentView: UIView = {
let v = UIView()
v.backgroundColor = .white
v.layer.cornerRadius = 12.0
v.clipsToBounds = true
return v
}()
lazy var messageLabel:UILabel = {
let lable = UILabel()
lable.textColor = UIColor.mainTextColor
lable.textAlignment = .left
lable.font = UIFont.PFSC_R(ofSize:14)
lable.text = "港小宝正在思考…"
return lable
}()
let loadingImgView: LottieAnimationView! = {
let lottieView = LottieAnimationView(name: "ai_chat_loading")
lottieView.loopMode = .loop
lottieView.contentMode = .scaleAspectFit
lottieView.play()
return lottieView
}()
lazy var shadowView: YHAIChatShadowView = {
let v = YHAIChatShadowView()
return v
}()
var rightAngleView: UIView = {
let v = UIView()
v.backgroundColor = .white
return v
}()
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
func setupUI() {
selectionStyle = .none
contentView.backgroundColor = .clear
backgroundColor = .clear
contentView.addSubview(shadowView)
contentView.addSubview(rightAngleView)
contentView.addSubview(whiteContentView)
whiteContentView.addSubview(messageLabel)
whiteContentView.addSubview(loadingImgView)
shadowView.snp.makeConstraints { make in
make.edges.equalTo(whiteContentView)
}
rightAngleView.snp.makeConstraints { make in
make.top.left.equalTo(whiteContentView)
make.width.height.equalTo(15)
}
whiteContentView.snp.makeConstraints { make in
make.left.equalTo(20)
make.right.lessThanOrEqualTo(-20)
make.top.equalTo(20)
make.bottom.equalTo(-20)
}
messageLabel.snp.makeConstraints { make in
make.left.equalTo(16)
make.right.equalTo(-16)
make.top.equalTo(16)
}
loadingImgView.snp.makeConstraints { make in
make.left.equalTo(messageLabel)
make.width.equalTo(26)
make.height.equalTo(18)
make.top.equalTo(messageLabel.snp.bottom).offset(6)
make.bottom.equalTo(-16)
}
}
}
...@@ -34,8 +34,8 @@ class YHAliYunLogManager { ...@@ -34,8 +34,8 @@ class YHAliYunLogManager {
static let SLS_PROJECT_DEV = "dev-yinhe-app" static let SLS_PROJECT_DEV = "dev-yinhe-app"
static let SLS_PROJECT_RELEASE = "release-yinhe-app" static let SLS_PROJECT_RELEASE = "release-yinhe-app"
static let SLS_LOG_STORE = "yinhe_logstore" static let SLS_LOG_STORE = "yinhe_logstore"
static let SLS_KEY = "LTAI5tPV7UEB2Dqyaippchbq" static let SLS_KEY = "LTAI5tRiRFQGiM3DtDVpjHWr"
static let SLS_SECRET = "K6AOXrVnLzb0Zn6HtHm9qFv8DfmEFv" static let SLS_SECRET = "f9Uc3LNhxDWwlBS7rmdg4UPCTUL57M"
static let shared = createManager() static let shared = createManager()
private var config: LogProducerConfig? private var config: LogProducerConfig?
......
...@@ -643,14 +643,14 @@ extension YHApplyActivityAlert { ...@@ -643,14 +643,14 @@ extension YHApplyActivityAlert {
func addKeyBoardNotify() { func addKeyBoardNotify() {
IQKeyboardManager.shared.enable = false IQKeyboardManager.shared.isEnabled = false
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(_:)), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
} }
func removeNotify() { func removeNotify() {
IQKeyboardManager.shared.enable = true IQKeyboardManager.shared.isEnabled = true
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
} }
......
...@@ -77,6 +77,7 @@ class YHHomeIdentityViewController: YHBaseViewController { ...@@ -77,6 +77,7 @@ class YHHomeIdentityViewController: YHBaseViewController {
self.noDataView.snp.makeConstraints { make in self.noDataView.snp.makeConstraints { make in
make.left.right.top.bottom.equalToSuperview() make.left.right.top.bottom.equalToSuperview()
} }
} }
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {
...@@ -92,6 +93,7 @@ class YHHomeIdentityViewController: YHBaseViewController { ...@@ -92,6 +93,7 @@ class YHHomeIdentityViewController: YHBaseViewController {
} }
} }
extension YHHomeIdentityViewController: JXSegmentedListContainerViewListDelegate { extension YHHomeIdentityViewController: JXSegmentedListContainerViewListDelegate {
func listView() -> UIView { func listView() -> UIView {
return view return view
......
...@@ -86,7 +86,7 @@ class YHOtherServiceViewController: YHBaseViewController { ...@@ -86,7 +86,7 @@ class YHOtherServiceViewController: YHBaseViewController {
} else if classID == 4 { } else if classID == 4 {
gk_navTitle = "办理攻略" gk_navTitle = "办理攻略"
} else if classID == 5 { } else if classID == 5 {
gk_navTitle = "客户心声" gk_navTitle = "客户成功"
} }
gk_navBackgroundColor = .white gk_navBackgroundColor = .white
gk_navBarAlpha = 1.0 gk_navBarAlpha = 1.0
......
This diff is collapsed.
...@@ -31,8 +31,8 @@ class YHHomeBannerIndicatorView: UIView { ...@@ -31,8 +31,8 @@ class YHHomeBannerIndicatorView: UIView {
} }
private var arrViews : [UIView] = [] private var arrViews : [UIView] = []
private var normalColor : UIColor = UIColor(hex: 0xffffff, alpha: 0.3) var normalColor : UIColor = UIColor(hex: 0xffffff, alpha: 0.3)
private var selectedColor : UIColor = UIColor(hex: 0xffffff) var selectedColor : UIColor = UIColor(hex: 0xffffff)
lazy var subHoldView: UIView = { lazy var subHoldView: UIView = {
let view = UIView() let view = UIView()
......
...@@ -20,4 +20,5 @@ struct YHContactItemModel : SmartCodable { ...@@ -20,4 +20,5 @@ struct YHContactItemModel : SmartCodable {
var status: Int = 0 var status: Int = 0
var product_type: Int = 0 var product_type: Int = 0
var isOther: Bool = false //是否协作单 var isOther: Bool = false //是否协作单
var service_name: String = ""
} }
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment