调度APP车辆在线通报及上线提醒,查看照片部分功能优化,车辆监控页面,新订单提醒

This commit is contained in:
ddisfriend
2025-10-17 11:49:31 +08:00
parent a3c631806d
commit 2368bfdb4e
25 changed files with 697 additions and 48 deletions

View File

@@ -67,6 +67,7 @@
795B949B2BECDB56008F3205 /* NewTraining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 795B949A2BECDB56008F3205 /* NewTraining.swift */; };
797484782DA67515003EEB47 /* NewTraningViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 797484772DA67515003EEB47 /* NewTraningViewModel.swift */; };
79B966382AB0651C00308A8D /* VehicleLogoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79B966372AB0651C00308A8D /* VehicleLogoutView.swift */; };
79BF24412E9CEFB300FA5F1E /* OnlineVehiclesEntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BF24402E9CEFB300FA5F1E /* OnlineVehiclesEntryView.swift */; };
79CB07CC2AA8465A00154B61 /* UserPermission.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79CB07CB2AA8465A00154B61 /* UserPermission.swift */; };
79CECC122A89BD1A00B95D8B /* MessageCenterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79CECC112A89BD1A00B95D8B /* MessageCenterController.swift */; };
79CECC192A89EE6A00B95D8B /* ReviewFailedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79CECC182A89EE6A00B95D8B /* ReviewFailedController.swift */; };
@@ -204,6 +205,7 @@
795B949A2BECDB56008F3205 /* NewTraining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTraining.swift; sourceTree = "<group>"; };
797484772DA67515003EEB47 /* NewTraningViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTraningViewModel.swift; sourceTree = "<group>"; };
79B966372AB0651C00308A8D /* VehicleLogoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleLogoutView.swift; sourceTree = "<group>"; };
79BF24402E9CEFB300FA5F1E /* OnlineVehiclesEntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnlineVehiclesEntryView.swift; sourceTree = "<group>"; };
79CB07CB2AA8465A00154B61 /* UserPermission.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPermission.swift; sourceTree = "<group>"; };
79CECC112A89BD1A00B95D8B /* MessageCenterController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCenterController.swift; sourceTree = "<group>"; };
79CECC182A89EE6A00B95D8B /* ReviewFailedController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewFailedController.swift; sourceTree = "<group>"; };
@@ -324,6 +326,7 @@
79FB75EF2A98A26C00DB00A4 /* AcceptOrderTool.swift */,
792EE0942AA74E0A00A212AB /* PushNotiCommonView.swift */,
792EE0962AA74E5800A212AB /* PushNotiCommonTool.swift */,
79BF24402E9CEFB300FA5F1E /* OnlineVehiclesEntryView.swift */,
);
path = View;
sourceTree = "<group>";
@@ -1160,6 +1163,7 @@
79FB76152A9DEE7400DB00A4 /* RefuseOrderConfirmView.swift in Sources */,
79FB76262A9F0A0000DB00A4 /* BackgroundTask.swift in Sources */,
79DD0DB12A94B3DB00768FE7 /* EmptyView.swift in Sources */,
79BF24412E9CEFB300FA5F1E /* OnlineVehiclesEntryView.swift in Sources */,
79DD0DAA2A9481BC00768FE7 /* NotificationAuthTool.swift in Sources */,
794FBB1C2A8F4DE900D57BB8 /* MessageView.swift in Sources */,
794FBB142A8F045F00D57BB8 /* MineController.swift in Sources */,

View File

@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "online_entry_pin.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "online_entry_pin@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "online_entry_pin@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "online_entry_background.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "online_entry_background@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "online_entry_background@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "vehichleMonitoring_contract_16.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "vehichleMonitoring_contract_16@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "vehichleMonitoring_contract_16@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

View File

@@ -14,7 +14,7 @@ public let ENTRY = Entry.default
open class Entry {
public static let `default` = Entry()
func showLoginNoticeEntry(view:UIView,name:String? = nil) {
func showOnlineVehiclesEntry(view:UIView,name:String? = nil) {
var attributes = EKAttributes()
attributes = .centerFloat
attributes.name = name
@@ -22,7 +22,7 @@ open class Entry {
attributes.displayMode = .inferred
attributes.displayDuration = .infinity
attributes.screenBackground = .color(color: .clear)
attributes.entryBackground = .color(color: .white)
attributes.entryBackground = .color(color: .clear)
attributes.screenInteraction = .absorbTouches
attributes.entryInteraction = .absorbTouches
attributes.scroll = .disabled
@@ -48,8 +48,8 @@ open class Entry {
)
)
attributes.positionConstraints.size = .init(
width: .constant(value: auto(280)),
height: .constant(value: auto(130))
width: .fill,
height: .fill
)
attributes.positionConstraints.verticalOffset = 0
attributes.positionConstraints.safeArea = .overridden

View File

@@ -132,6 +132,10 @@ open class Tool {
}
}
func playVoiceWith(broadcast: String) {
DDSS.play(text: broadcast, name: "broadcast")
}
func stopVoice() {
DDAS.stopSound(name: "sound")
DDSS.stop(name: "broadcast")

View File

@@ -81,4 +81,8 @@ open class ApiList {
public let getAppeal = "/supplierAppV2/dispatchApp/order/getAppeal"
public let saveAppeal = "/driverApp/task/saveAppeal"
public let onlineReminder = "/supplierAppV2/dispatchApp/alarm/onlineReminder"
public let onlineReminderRead = "/supplierAppV2/dispatchApp/alarm/onlineReminderRead"
}

View File

@@ -267,3 +267,11 @@ public struct SaveAppealParameters : Encodable {
var vehicleId : Int?
var msg : String?
}
public struct OnlineReminderParameters : Encodable {
var supplierId : Int?
}
public struct OnlineReminderReadParameters : Encodable {
var id : Int?
}

View File

@@ -168,4 +168,12 @@ open class RequestList {
func saveAppeal<P:Encodable>(parameters:P) -> Single<ResponseModel<SaveAppealModel>?> {
return DDAF.get(urlString: HOST+API.saveAppeal,parameters: parameters,encoding: URLEncodedFormParameterEncoder.default,headers: [tokenHeader()],responseType: ResponseModel<SaveAppealModel>.self)
}
func onlineReminder<P:Encodable>(parameters:P) -> Single<ResponseModel<MessageReminderListDataModel>?> {
return DDAF.get(urlString: HOST+API.onlineReminder,parameters: parameters,encoding: URLEncodedFormParameterEncoder.default,headers: [tokenHeader()],responseType: ResponseModel<MessageReminderListDataModel>.self)
}
func onlineReminderRead<P:Encodable>(parameters:P) -> Single<ResponseModel<String>?> {
return DDAF.get(urlString: HOST+API.onlineReminderRead,parameters: parameters,encoding: URLEncodedFormParameterEncoder.default,headers: [tokenHeader()],responseType: ResponseModel<String>.self)
}
}

View File

@@ -185,6 +185,9 @@ public class VehicleMonitorListDataModel : Decodable {
var alarmType : AlarmType?
var rosterStartTime : String?
var rosterEndTime : String?
var rosterType : String?
var endTime : String?
var startTime : String?
var lat : String?
var lon : String?
var orderCode : String?
@@ -229,7 +232,11 @@ public class VehicleMonitorListDataModel : Decodable {
var destinationLongitude : Double?
var destinationLatitude : Double?
var destinationAddress : String?
}
var leftTimeB : Double?
var leftTimeC : Double?
var distance : Double?
var mileageBc : Double?
var contractName : String? }
}
public class OrderPhotoListDataModel : Decodable {

View File

@@ -20,6 +20,7 @@ open class AcceptOrderTool : NSObject {
acceptOrderView.titleLabel.text = "新订单"
acceptOrderView.contentLabel.text = TOOL.getOrderString(userInfo: userInfo)
acceptOrderView.readButton.setTitle("查看", for: .normal)
acceptOrderView.cancelButton.setTitle("稍后查看", for: .normal)
acceptOrderView.readButton.rx.tap
.observe(on: MainScheduler.instance)
.subscribe(onNext: {

View File

@@ -18,9 +18,8 @@ open class AcceptOrderView : DDView {
public let contentLabel : DDLabel
public let horizontalLine : DDView
public let verticalLine : DDView
public let refuseButton : DDButton
public let acceptButton : DDButton
public let readButton : DDButton
public let cancelButton : DDButton
public override init(frame: CGRect) {
radiusView = DDView.init()
scrollView = DDScrollView()
@@ -29,9 +28,8 @@ open class AcceptOrderView : DDView {
contentLabel = DDLabel.dd_init(withText: "", font: .mediumFont(auto(14)), textColor: .hex("203053"))
horizontalLine = DDView()
verticalLine = DDView()
refuseButton = DDButton.dd_initCustom()
acceptButton = DDButton.dd_initCustom()
readButton = DDButton.dd_initCustom()
cancelButton = DDButton.dd_initCustom()
super.init(frame: frame)
radiusView.layer.cornerRadius = auto(10)
radiusView.layer.masksToBounds = true
@@ -47,20 +45,14 @@ open class AcceptOrderView : DDView {
verticalLine.backgroundColor = .hex("979797").alpha(0.35)
verticalLine.isHidden = true
radiusView.addSubview(verticalLine)
refuseButton.isHidden = true
refuseButton.titleLabel?.font = .mediumFont(auto(14))
refuseButton.backgroundColor = .white
refuseButton.setTitleColor(.black.alpha(0.3), for: .normal)
radiusView.addSubview(refuseButton)
acceptButton.titleLabel?.font = .mediumFont(auto(14))
acceptButton.backgroundColor = .white
acceptButton.setTitleColor(.black.alpha(0.3), for: .normal)
acceptButton.isHidden = true
radiusView.addSubview(acceptButton)
readButton.titleLabel?.font = .mediumFont(auto(14))
readButton.backgroundColor = .white
readButton.setTitleColor(.hex("1D64D2"), for: .normal)
radiusView.addSubview(readButton)
cancelButton.titleLabel?.font = .mediumFont(auto(14))
cancelButton.backgroundColor = .white
cancelButton.setTitleColor(.hex("1D64D2"), for: .normal)
cancelButton.addSubview(cancelButton)
radiusView.snp.makeConstraints { make in
make.edges.equalToSuperview()
@@ -103,23 +95,18 @@ open class AcceptOrderView : DDView {
make.width.equalTo(1)
}
refuseButton.snp.makeConstraints { make in
make.bottom.equalToSuperview()
make.left.equalToSuperview()
make.top.equalTo(horizontalLine.snp.bottom)
make.right.equalTo(verticalLine.snp.left)
}
acceptButton.snp.makeConstraints { make in
readButton.snp.makeConstraints { make in
make.bottom.equalToSuperview()
make.right.equalToSuperview()
make.top.equalTo(horizontalLine.snp.bottom)
make.left.equalTo(verticalLine.snp.right)
}
readButton.snp.makeConstraints { make in
cancelButton.snp.makeConstraints { make in
make.bottom.equalToSuperview()
make.left.equalToSuperview()
make.top.equalTo(horizontalLine.snp.bottom)
make.left.right.bottom.equalToSuperview()
make.right.equalTo(verticalLine.snp.left)
}
}

View File

@@ -0,0 +1,129 @@
//
// OnlineVehiclesEntryView.swift
// OrderScheduling
//
// Created by on 2025/10/13.
//
import UIKit
import SnapKit
/// 线
final class OnlineVehiclesEntryView: UIView {
// MARK: - Public
///
var onConfirm: (() -> Void)?
// MARK: - UI
private let pinImageView: UIImageView = {
let iv = UIImageView(image: UIImage(named: "online_entry_pin"))
iv.contentMode = .scaleAspectFit
return iv
}()
private let backgroundImageView: UIImageView = {
let iv = UIImageView(image: UIImage(named: "online_entry_background"))
iv.contentMode = .scaleAspectFit
return iv
}()
private let cardView: UIView = {
let v = UIView()
v.backgroundColor = .white
v.layer.cornerRadius = 16
v.layer.shadowColor = UIColor.black.cgColor
v.layer.shadowOpacity = 0.12
v.layer.shadowRadius = 10
v.layer.shadowOffset = CGSize(width: 0, height: 6)
return v
}()
private let titleLabel: UILabel = {
let l = UILabel()
l.text = "实时车辆在线情况"
l.textAlignment = .center
l.font = .boldSystemFont(ofSize: 16)
l.textColor = UIColor(hex: "#040404")
return l
}()
///
private let contentLabel: UILabel = {
let l = UILabel()
l.numberOfLines = 0
l.textAlignment = .left
l.textColor = UIColor(hex: "#4C5361")
l.font = .dd_systemFont(ofSize: 14, weight: .medium)
return l
}()
private let confirmButton: UIButton = {
let b = UIButton(type: .system)
b.setTitle("已知晓", for: .normal)
b.setTitleColor(.white, for: .normal)
b.titleLabel?.font = .boldSystemFont(ofSize: 16)
b.backgroundColor = UIColor(hex: "#3174CE")
b.layer.cornerRadius = 24
b.layer.masksToBounds = true
return b
}()
// MARK: - Init
override init(frame: CGRect) {
super.init(frame: frame)
buildUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
buildUI()
}
convenience init(content: String?) {
self.init(frame: .zero)
contentLabel.text = content
}
func buildUI() {
backgroundColor = .clear
addSubview(cardView)
cardView.addSubview(backgroundImageView)
cardView.addSubview(pinImageView)
cardView.addSubview(titleLabel)
cardView.addSubview(contentLabel)
cardView.addSubview(confirmButton)
cardView.snp.makeConstraints { make in
make.centerY.centerX.equalToSuperview()
make.left.right.equalToSuperview().inset(30)
}
backgroundImageView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
}
pinImageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.centerY.equalTo(cardView.snp.top)
make.size.equalTo(CGSize(width: 88, height: 88))
}
titleLabel.snp.makeConstraints { make in
make.top.equalTo(pinImageView.snp.bottom).offset(20)
make.left.right.equalTo(cardView).inset(20)
}
contentLabel.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(12)
make.left.right.equalTo(titleLabel)
}
confirmButton.snp.makeConstraints { make in
make.top.equalTo(contentLabel.snp.bottom).offset(18)
make.left.right.equalTo(titleLabel)
make.height.equalTo(48)
make.bottom.equalTo(cardView.snp.bottom).inset(20)
}
}
}

View File

@@ -86,9 +86,7 @@ extension RescueController {
USER.refreshTokenSub
.subscribe(onNext: {[weak self] _ in
NewTraining.default.newTrainingRelay.accept(nil)
self?.appBannerRelay.accept(nil)
self?.appPushRecordRelay.accept(nil)
self?.excuseRelay()
})
.disposed(by: disposeBag)
@@ -138,19 +136,55 @@ extension RescueController {
})
.disposed(by: disposeBag)
onlineReminderRelay
.flatMapLatest { _ in
return RQ.onlineReminder(parameters: OnlineReminderParameters(supplierId: UserData.default.supplierId))
}
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] response in
guard let self = self else { return }
if response?.success == true, let data = response?.data, let content = data.content {
let entryView = OnlineVehiclesEntryView(content: content)
entryView.onConfirm = { [weak self] in
guard let self = self else { return }
RQ.onlineReminderRead(parameters: OnlineReminderReadParameters(id: response?.data?.id))
.observe(on: MainScheduler.instance)
.subscribe(onSuccess: {[weak self] readResponse in
guard let self = self else { return }
if readResponse?.success == true {
Entry.default.dismiss(name: "onlineVehiclesEntryView")
} else {
self.view.dd_makeToast(readResponse?.msg)
}
})
.disposed(by: self.disposeBag)
}
Entry.default.showOnlineVehiclesEntry(view: entryView, name: "onlineVehiclesEntryView")
Tool.default.playVoiceWith(broadcast: content)
} else {
self.view.dd_makeToast(response?.msg)
}
})
.disposed(by: disposeBag)
// tabBar
preRefreshRelay
.observe(on: MainScheduler.instance)
.subscribe(onNext: {[weak self] _ in
NewTraining.default.newTrainingRelay.accept(nil)
self?.appBannerRelay.accept(nil)
self?.appPushRecordRelay.accept(nil)
self?.excuseRelay()
MCOUNT.newestMessage()
self?.categoryView.reloadData()
})
.disposed(by: disposeBag)
}
func excuseRelay() {
NewTraining.default.newTrainingRelay.accept(nil)
appBannerRelay.accept(nil)
appPushRecordRelay.accept(nil)
onlineReminderRelay.accept(nil)
}
}
extension RescueController : TYCyclePagerViewDataSource, TYCyclePagerViewDelegate {
@@ -1182,6 +1216,8 @@ class RescueController : ZDViewController {
private var appBannerRelay = ReplayRelay<Any?>.create(bufferSize: 1)
private var bannerDataSources : [ConfigByCodeDataModel.ConfigByCodeBannerModel] = []
private var onlineReminderRelay = ReplayRelay<Any?>.create(bufferSize: 1)
override func viewDidLoad() {
super.viewDidLoad()
dd_navigationBarBackgroundColor = .hex("354683")

View File

@@ -12,6 +12,8 @@ import RxSwift
import ZLPhotoBrowser
import RxCocoa
import RxRelay
import SnapKit
import DDUtilsSwiftKit_Private
extension AdditionalPhotoController{
func addActions() {
@@ -79,6 +81,55 @@ extension AdditionalPhotoController{
}
})
.disposed(by: disposeBag)
// HUD
additionalPhotoView.actionView.save.rx.tap
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] in
guard let self = self else { return }
let images = self.collectSelectedImages()
guard !images.isEmpty else { self.view.dd_makeToast("请先选择照片"); return }
// HUD
let hud = ZLProgressHUD.show()
let group = DispatchGroup()
var successCount = 0
images.forEach { img in
group.enter()
ZLPhotoManager.saveImageToAlbum(image: img) { isSuccess, _ in
if isSuccess { successCount += 1 }
group.leave()
}
}
group.notify(queue: .main) {
hud.hide()
if successCount == images.count {
self.view.dd_makeToast("已保存到相册")
} else if successCount == 0 {
// 沿 alert
self.presentSaveErrorAlert()
} else {
self.view.dd_makeToast("已保存 \(successCount)/\(images.count)")
}
}
})
.disposed(by: disposeBag)
//
additionalPhotoView.actionView.share.rx.tap
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] in
guard let self = self else { return }
let images = self.collectSelectedImages()
guard !images.isEmpty else { self.view.dd_makeToast("请先选择照片"); return }
let activity = UIActivityViewController(activityItems: images, applicationActivities: nil)
activity.modalPresentationStyle = .formSheet
self.present(activity, animated: true)
})
.disposed(by: disposeBag)
}
}
@@ -127,6 +178,33 @@ extension AdditionalPhotoController : UICollectionViewDelegate, UICollectionView
})
.disposed(by: cell!.disposeBag)
cell?.takePhotoButton.isHidden = !canModify
// UI
let hasImage = (itemModel.photoUrl != nil) || ((cell?.uploadImageView.image) != nil)
cell?.selectButton.isHidden = !isSelecting || !hasImage
let isChecked = selectedIndexPaths.contains(indexPath)
cell?.selectButton.isSelected = isChecked
cell?.selectButton.setTitleColor(isChecked ? .white : UIColor.hex("#206FFF"), for: .normal)
cell?.selectButton.rx.tap
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] in
guard let self = self else { return }
//
guard let c = collectionView.cellForItem(at: indexPath) as? AdditionalPhotoCell, c.uploadImageView.isHidden == false else { return }
if self.selectedIndexPaths.contains(indexPath) {
self.selectedIndexPaths.remove(indexPath)
} else {
self.selectedIndexPaths.insert(indexPath)
}
if let c = collectionView.cellForItem(at: indexPath) as? AdditionalPhotoCell {
let checked = self.selectedIndexPaths.contains(indexPath)
c.selectButton.isSelected = checked
c.selectButton.setTitleColor(checked ? .white : UIColor.hex("#206FFF"), for: .normal)
}
self.updateActionViewVisibility(animated: true)
})
.disposed(by: cell!.disposeBag)
return cell!
}
@@ -138,6 +216,26 @@ extension AdditionalPhotoController : UICollectionViewDelegate, UICollectionView
}
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if isSelecting {
//
if let c = collectionView.cellForItem(at: indexPath) as? AdditionalPhotoCell, c.uploadImageView.isHidden {
return
}
if selectedIndexPaths.contains(indexPath) {
selectedIndexPaths.remove(indexPath)
} else {
selectedIndexPaths.insert(indexPath)
}
if let c = collectionView.cellForItem(at: indexPath) as? AdditionalPhotoCell {
let checked = selectedIndexPaths.contains(indexPath)
c.selectButton.isSelected = checked
c.selectButton.setTitleColor(checked ? .white : UIColor.hex("#206FFF"), for: .normal)
}
updateActionViewVisibility(animated: true)
return
}
// 沿
let cell = self.collectionView(collectionView, cellForItemAt: indexPath) as? AdditionalPhotoCell
if let image = cell?.uploadImageView.image {
let vc = ZLImagePreviewController.init(datas: [image as Any],showSelectBtn: false,showBottomView: false)
@@ -158,6 +256,9 @@ open class AdditionalPhotoController : ZDViewController {
private let taskOrderId : Int
private let canModify : Bool
private var resultArr : [OrderPhotoListDataModel] = []
private var isSelecting = false
private var selectedIndexPaths = Set<IndexPath>()
private var selectButton: UIButton?
public init(userOrderId: Int, orderCode: String, taskOrderId: Int, canModify:Bool){
self.userOrderId = userOrderId
self.orderCode = orderCode
@@ -179,6 +280,14 @@ open class AdditionalPhotoController : ZDViewController {
addSubviews()
addActions()
let selectButton = UIButton(type: .system)
selectButton.setTitle("选择", for: .normal)
selectButton.setTitleColor(.white.alpha(0.7), for: .normal)
selectButton.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
selectButton.addTarget(self, action: #selector(toggleSelectMode), for: .touchUpInside)
let barButton = UIBarButtonItem(customView: selectButton)
navigationItem.rightBarButtonItem = barButton
self.selectButton = selectButton
}
func addSubviews() {
@@ -194,11 +303,80 @@ open class AdditionalPhotoController : ZDViewController {
additionalPhotoView.collectionView.delegate = self
additionalPhotoView.collectionView.dataSource = self
}
@objc private func toggleSelectMode() {
if isSelecting { cancelSelectMode() } else { enterSelectMode() }
}
private func enterSelectMode() {
isSelecting = true
selectedIndexPaths.removeAll()
selectButton?.setTitle("取消", for: .normal)
updateActionViewVisibility(animated: true)
additionalPhotoView.collectionView.reloadData()
}
private func cancelSelectMode() {
isSelecting = false
selectedIndexPaths.removeAll()
selectButton?.setTitle("选择", for: .normal)
updateActionViewVisibility(animated: true)
additionalPhotoView.collectionView.reloadData()
}
private func updateActionViewVisibility(animated: Bool) {
let shouldShow = isSelecting && !selectedIndexPaths.isEmpty
let targetHeight: CGFloat = shouldShow ? 50 : 0
additionalPhotoView.actionView.isHidden = (targetHeight == 0)
additionalPhotoView.actionHeightConstraint?.update(offset: targetHeight)
if isSelecting {
if !selectedIndexPaths.isEmpty {
selectButton?.setTitle("取消", for: .normal)
} else {
selectButton?.setTitle("取消", for: .normal)
}
} else {
selectButton?.setTitle("选择", for: .normal)
}
if animated {
UIView.animate(withDuration: 0.25) { self.view.layoutIfNeeded() }
} else {
self.view.layoutIfNeeded()
}
}
private func collectSelectedImages() -> [UIImage] {
var images: [UIImage] = []
for indexPath in selectedIndexPaths {
let sectionModel = resultArr[indexPath.section]
let itemModel = sectionModel.photoList[indexPath.item]
if let cell = additionalPhotoView.collectionView.cellForItem(at: indexPath) as? AdditionalPhotoCell,
let img = cell.uploadImageView.image {
images.append(img)
} else if let urlString = itemModel.photoUrl,
let url = URL(string: urlString),
let data = try? Data(contentsOf: url),
let img = UIImage(data: data) {
images.append(img)
}
}
return images
}
private func presentSaveErrorAlert() {
let alert = UIAlertController(title: nil, message: "图片保存失败,请检查相册权限", preferredStyle: .alert)
let cancel = UIAlertAction(title: "取消", style: .cancel, handler: nil)
let confirm = UIAlertAction(title: "确定", style: .default, handler: { _ in
UrlLinks.default.openSetting()
})
alert.addAction(cancel)
alert.addAction(confirm)
self.present(alert, animated: true)
}
}
open class AdditionalPhotoView : DDView {
public var collectionView : DDCollectionView
public var actionView : AdditionalPhotoActionView
public var actionHeightConstraint : Constraint?
public override init(frame: CGRect) {
let flowLayout = UICollectionViewFlowLayout.init()
flowLayout.itemSize = CGSize(width: auto(165), height: auto(100))
@@ -207,13 +385,20 @@ open class AdditionalPhotoView : DDView {
flowLayout.minimumLineSpacing = auto(10)
flowLayout.sectionInset = UIEdgeInsets(top: auto(10), left: auto(15), bottom: auto(10), right: auto(15))
collectionView = DDCollectionView(frame: .zero, collectionViewLayout: flowLayout)
actionView = AdditionalPhotoActionView()
super.init(frame: frame)
collectionView.backgroundColor = .white
addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.top.left.right.bottom.equalToSuperview()
make.top.left.right.equalToSuperview()
}
actionView.isHidden = true
addSubview(actionView)
actionView.snp.makeConstraints { make in
make.top.equalTo(collectionView.snp.bottom)
make.left.right.bottom.equalToSuperview()
self.actionHeightConstraint = make.height.equalTo(0).constraint
}
}
@@ -230,6 +415,7 @@ open class AdditionalPhotoCell : DDCollectionViewCell {
public let stateLabel : DDLabel
public let takePhotoButton : DDButton
public let uploadImageView : DDImageView
public let selectButton : DDButton
public var disposeBag = DisposeBag()
public override init(frame: CGRect) {
@@ -240,8 +426,10 @@ open class AdditionalPhotoCell : DDCollectionViewCell {
stateLabel = DDLabel.dd_init(withText: "", font: .regularFont(auto(12)), textColor: .white)
takePhotoButton = DDButton.dd_initCustom()
uploadImageView = DDImageView.init()
selectButton = DDButton.dd_initCustom()
super.init(frame: .zero)
titleLabel.numberOfLines = 2
addSubview(titleLabel)
backgroundImageView.layer.borderColor = UIColor.hex("000000").alpha(0.1).cgColor
backgroundImageView.layer.borderWidth = 0.5
@@ -263,8 +451,14 @@ open class AdditionalPhotoCell : DDCollectionViewCell {
takePhotoButton.setImage(UIImage(named: "additionalPhotot_takePhoto"), for: .normal)
backgroundImageView.addSubview(takePhotoButton)
backgroundImageView.addSubview(selectButton)
selectButton.setImage(UIImage(named: "refuseOrder_reason_unselected"), for: .normal)
selectButton.setImage(UIImage(named: "refuseOrder_reason_selected"), for: .selected)
selectButton.isHidden = true
titleLabel.snp.makeConstraints { make in
make.left.equalTo(auto(5))
make.right.equalTo(-auto(5))
make.top.equalTo(0)
}
@@ -298,6 +492,10 @@ open class AdditionalPhotoCell : DDCollectionViewCell {
make.edges.equalToSuperview()
}
selectButton.snp.makeConstraints { make in
make.top.right.equalToSuperview().inset(0)
make.width.height.equalTo(auto(30))
}
}
public required init?(coder: NSCoder) {
@@ -306,6 +504,8 @@ open class AdditionalPhotoCell : DDCollectionViewCell {
open override func prepareForReuse() {
super.prepareForReuse()
selectButton.isSelected = false
selectButton.isHidden = true
disposeBag = DisposeBag()
}
}
@@ -328,3 +528,56 @@ open class AdditionalPhotoHeaderView : DDCollectionViewCell {
fatalError("init(coder:) has not been implemented")
}
}
open class AdditionalPhotoActionView : UIView {
public let save : UIButton = UIButton(type: .custom)
public let share : UIButton = UIButton(type: .custom)
private let topSeparator = UIView()
private let middleSeparator = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
buildUI()
}
required public init?(coder: NSCoder) {
super.init(coder: coder)
buildUI()
}
func buildUI() {
// Top horizontal separator
topSeparator.backgroundColor = UIColor.hex("E5E6EB")
addSubview(topSeparator)
topSeparator.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.height.equalTo(1.0 / UIScreen.main.scale)
}
save.setTitle("保存至相册", for: .normal)
save.setTitleColor(.black, for: .normal)
save.titleLabel?.font = .dd_systemFont(ofSize: 16, weight: .medium)
addSubview(save)
share.setTitle("分享", for: .normal)
share.setTitleColor(.black, for: .normal)
share.titleLabel?.font = .dd_systemFont(ofSize: 16, weight: .medium)
addSubview(share)
save.snp.makeConstraints { make in
make.centerX.equalToSuperview().multipliedBy(0.5)
make.centerY.equalToSuperview()
}
share.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.centerX.equalToSuperview().multipliedBy(1.5)
}
// Middle vertical separator
middleSeparator.backgroundColor = UIColor.hex("E5E6EB")
addSubview(middleSeparator)
middleSeparator.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.centerY.equalToSuperview()
make.width.equalTo(1.0 / UIScreen.main.scale)
make.height.equalTo(24)
}
}
}

View File

@@ -18,8 +18,8 @@ import DDUIGestureRecognizer
import DDUtilsSwiftKit_Private
fileprivate let vehicleMonitoringPannelViewHeight = auto(300)
fileprivate let vehicleMonitoringPannelCategoryView = auto(40)
fileprivate let vehicleMonitoringListDetailViewHeight = auto(190)
fileprivate let vehicleMonitoringPannelCategoryViewHeight = auto(40)
fileprivate let vehicleMonitoringListDetailViewHeight = auto(270)
fileprivate let vehicleMonitoringPointAnnotationW = auto(120)
fileprivate let vehicleMonitoringPointAnnotationNameLeftInset = auto(35)
@@ -768,8 +768,39 @@ extension VehicleMonitoringController {
///
if let number = vehicleModel.number, number.isEmpty == false {
vehicleMonitoringListDetailView.videoButton.isHidden = false
vehicleMonitoringListDetailView.videoButton.snp.updateConstraints { make in
make.left.equalTo(vehicleMonitoringListDetailView.settingButton.snp.right).offset(auto(10))
make.width.equalTo(auto(23))
}
}else{
vehicleMonitoringListDetailView.videoButton.isHidden = true
vehicleMonitoringListDetailView.videoButton.snp.updateConstraints { make in
make.left.equalTo(vehicleMonitoringListDetailView.settingButton.snp.right).offset(0)
make.width.equalTo(0)
}
}
if let terminalType = vehicleModel.terminalType {
vehicleMonitoringListDetailView.locationLabel.text = "定位:\(terminalType)"
}
var workScheduleString = ""
if let startTime = vehicleModel.startTime, let endTime = vehicleModel.endTime {
workScheduleString = "\(startTime)-\(endTime)"
}
if workScheduleString.count > 0 {
if let rosterType = vehicleModel.rosterType {
workScheduleString = workScheduleString.appending("/\(rosterType)")
}
}else{
workScheduleString = vehicleModel.rosterType ?? ""
}
if workScheduleString.count == 0 {
vehicleMonitoringListDetailView.workScheduleTitle.text = nil
vehicleMonitoringListDetailView.workScheduleContent.text = nil
}else{
vehicleMonitoringListDetailView.workScheduleTitle.text = "排班:"
vehicleMonitoringListDetailView.workScheduleContent.text = workScheduleString
}
/// maxpannelView
@@ -959,7 +990,7 @@ open class VehicleMonitoringController : ZDViewController {
vehicleMonitoringView.vehicleMonitoringPannelView.categoryView.snp.remakeConstraints { make in
make.top.equalToSuperview().priority(.high)
make.left.right.equalToSuperview()
make.height.equalTo(vehicleMonitoringPannelCategoryView)
make.height.equalTo(vehicleMonitoringPannelCategoryViewHeight)
}
vehicleMonitoringListDetailView.layer.cornerRadius = vehicleMonitoringPannelViewCornerRadius
@@ -971,13 +1002,13 @@ open class VehicleMonitoringController : ZDViewController {
make.bottom.equalToSuperview()
})
let minDisplayHeight = vehicleMonitoringPannelCategoryView
let minDisplayHeight = vehicleMonitoringPannelCategoryViewHeight
let maxDisplayHeight = view.height - SafeAreaInsets.safeAreaInsetsTop - SafeAreaInsets.safeAreaInsetsBottom - auto(150)
let defaultDisplayHeight = vehicleMonitoringListDetailViewHeight
vehicleMonitoringView.maMapView.snp.remakeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview()
make.bottom.equalToSuperview().offset(-vehicleMonitoringPannelCategoryView + vehicleMonitoringPannelViewCornerRadius)
make.bottom.equalToSuperview().offset(-vehicleMonitoringPannelCategoryViewHeight + vehicleMonitoringPannelViewCornerRadius)
}
if pannelPanGes.panGesValue.expandLevel == .max {
@@ -1355,6 +1386,9 @@ class VehicleMonitoringListDetailView : DDView, JXCategoryListContainerViewDeleg
public let vehicleLabel : DDLabel
public let settingButton : DDButton
public let videoButton : DDButton
public let locationLabel : DDLabel
public let workScheduleTitle : DDLabel
public let workScheduleContent : DDLabel
public let nameLabel : DDLabel
public let callButton : DDButton
public let containerView : DDView
@@ -1380,7 +1414,10 @@ class VehicleMonitoringListDetailView : DDView, JXCategoryListContainerViewDeleg
settingButton.setBackgroundImage(UIImage(named: "vehicleMonitoring_setting"), for: .normal)
videoButton = DDButton.dd_initCustom()
videoButton.setBackgroundImage(UIImage(named: "vehicleMonitoring_video_icon"), for: .normal)
nameLabel = DDLabel.dd_init(withText: "", font: .regularFont(auto(14)), textColor: .hex("11142F"))
locationLabel = DDLabel.dd_init(withText: "", font: .regularFont(12), textColor: .hex("11142F"))
workScheduleTitle = DDLabel.dd_init(withText: "", font: .regularFont(12), textColor: .hex("11142F"))
workScheduleContent = DDLabel.dd_init(withText: "", font: .regularFont(12), textColor: .hex("11142F"))
nameLabel = DDLabel.dd_init(withText: "", font: .regularFont(auto(12)), textColor: .hex("11142F"))
callButton = DDButton.dd_initCustom()
callButton.setBackgroundImage(UIImage(named: "vehicleMonitor_call_cell"), for: .normal)
containerView = DDView()
@@ -1404,9 +1441,11 @@ class VehicleMonitoringListDetailView : DDView, JXCategoryListContainerViewDeleg
addSubview(vehicleLabel)
addSubview(settingButton)
addSubview(videoButton)
addSubview(locationLabel)
addSubview(nameLabel)
addSubview(callButton)
addSubview(workScheduleTitle)
addSubview(workScheduleContent)
containerView.backgroundColor = .hex("FAFAFA")
containerView.layer.cornerRadius = auto(5)
containerView.layer.masksToBounds = true
@@ -1427,7 +1466,7 @@ class VehicleMonitoringListDetailView : DDView, JXCategoryListContainerViewDeleg
containerView.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(auto(7))
make.top.equalTo(backButton.snp.bottom).offset(0)
make.top.equalTo(workScheduleContent.snp.bottom).offset(auto(5))
make.bottom.equalToSuperview().offset(-auto(10))
}
@@ -1498,6 +1537,11 @@ class VehicleMonitoringListDetailView : DDView, JXCategoryListContainerViewDeleg
make.height.equalTo(auto(13))
}
locationLabel.snp.makeConstraints { make in
make.left.equalTo(videoButton.snp.right).offset(auto(10))
make.centerY.equalTo(icon)
}
callButton.snp.makeConstraints { make in
make.right.equalTo(-auto(20))
make.centerY.equalTo(backButton)
@@ -1509,6 +1553,17 @@ class VehicleMonitoringListDetailView : DDView, JXCategoryListContainerViewDeleg
make.centerY.equalTo(callButton)
}
workScheduleTitle.snp.makeConstraints { make in
make.top.equalTo(backButton.snp.bottom)
make.left.equalTo(icon)
}
workScheduleContent.snp.makeConstraints { make in
make.left.equalTo(workScheduleTitle.snp.right).offset(auto(10))
make.right.lessThanOrEqualToSuperview().offset(-auto(20))
make.top.equalTo(workScheduleTitle)
}
previousButton.rx.tap
.observe(on: MainScheduler.instance)
.subscribe(onNext: {[weak self] in
@@ -1572,6 +1627,12 @@ class VMLDContainerView : DDView , JXCategoryListContentViewDelegate {
var incidentLabel : DDLabel
var destIcon : UIImageView
var destLabel : DDLabel
var contractImageView : DDImageView
var contractTitle : DDLabel
var contractContent : DDLabel
var distanceFromPointLabel : DDLabel
var remainTimeLabel : DDLabel
var distanceLabel : DDLabel
override init(frame: CGRect) {
orderNumButton = UIButton()
orderNumButton.setTitleColor(.hex("0E76F4"), for: .normal)
@@ -1583,6 +1644,12 @@ class VMLDContainerView : DDView , JXCategoryListContentViewDelegate {
incidentLabel = DDLabel.dd_init(withText: "", font: .regularFont(auto(12)), textColor: .hex("000000").alpha(0.7))
destIcon = UIImageView(image: UIImage(named: "vehichleMonitoring_terminal_16"))
destLabel = DDLabel.dd_init(withText: "", font: .regularFont(auto(12)), textColor: .hex("000000").alpha(0.7))
contractImageView = DDImageView(image: UIImage(named: "vehichleMonitoring_contract_16"))
contractTitle = DDLabel.dd_init(withText: "合同:", font: .regularFont(12), textColor: .hex("11142F"))
contractContent = DDLabel.dd_init(withText: "", font: .regularFont(12), textColor: .hex("11142F"))
distanceFromPointLabel = DDLabel()
remainTimeLabel = DDLabel()
distanceLabel = DDLabel()
super.init(frame: frame)
addSubview(orderNumButton)
@@ -1599,6 +1666,14 @@ class VMLDContainerView : DDView , JXCategoryListContentViewDelegate {
destLabel.numberOfLines = 0
destLabel.textAlignment = .left
addSubview(destLabel)
addSubview(contractImageView)
addSubview(contractTitle)
contractContent.textAlignment = .right
contractContent.numberOfLines = 2
addSubview(contractContent)
addSubview(distanceFromPointLabel)
addSubview(remainTimeLabel)
addSubview(distanceLabel)
orderNumButton.snp.makeConstraints { make in
make.left.equalTo(auto(20))
@@ -1625,10 +1700,27 @@ class VMLDContainerView : DDView , JXCategoryListContentViewDelegate {
make.left.right.equalToSuperview().inset(auto(20))
}
contractImageView.snp.makeConstraints { make in
make.left.equalTo(orderNumButton)
make.top.equalTo(line.snp.bottom).offset(auto(5))
make.width.height.equalTo(auto(15))
}
contractTitle.snp.makeConstraints { make in
make.left.equalTo(contractImageView.snp.right).offset(auto(10))
make.centerY.equalTo(contractImageView.snp.centerY)
}
contractContent.snp.makeConstraints { make in
make.left.equalTo(contractTitle.snp.right).offset(auto(10))
make.right.equalToSuperview().offset(-auto(20))
make.centerY.equalTo(contractImageView)
}
incidentIcon.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
incidentIcon.snp.makeConstraints { make in
make.left.equalTo(orderNumButton)
make.top.equalTo(line.snp.bottom).offset(auto(15))
make.top.equalTo(contractImageView.snp.bottom).offset(auto(15))
}
incidentLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
@@ -1652,6 +1744,22 @@ class VMLDContainerView : DDView , JXCategoryListContentViewDelegate {
make.right.equalTo(-auto(20))
make.bottom.greaterThanOrEqualTo(destIcon)
}
remainTimeLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.height.equalTo(auto(20))
make.top.equalTo(destLabel.snp.bottom).offset(auto(10))
}
distanceFromPointLabel.snp.makeConstraints { make in
make.left.equalTo(orderNumButton)
make.centerY.equalTo(remainTimeLabel)
}
distanceLabel.snp.makeConstraints { make in
make.right.equalTo(-auto(20))
make.centerY.equalTo(remainTimeLabel)
}
}
required init?(coder: NSCoder) {
@@ -1663,7 +1771,38 @@ class VMLDContainerView : DDView , JXCategoryListContentViewDelegate {
titleLabel.text = taskModel.serviceName
stateLabel.text = taskModel.taskStatusString
incidentLabel.text = taskModel.vehiclePointAddress
destLabel.text = taskModel.destinationAddress
destLabel.text = (taskModel.destinationAddress ?? "") + (taskModel.destinationAddress ?? "")
contractContent.text = taskModel.contractName
var point = ""
var leftTime : Int? = nil
if let leftTimeB = taskModel.leftTimeB {
point = "B"
leftTime = Int(leftTimeB)
}else if let leftTimeC = taskModel.leftTimeC {
point = "C"
leftTime = Int(leftTimeC)
}
let distanceFromPointAttributeString = NSMutableAttributedString(string: "距离\(point)点: ",attributes: [.foregroundColor : UIColor.hex("11142F"),.font : UIFont.regularFont(12)])
if let distance = taskModel.distance {
distanceFromPointAttributeString.append(NSMutableAttributedString(string: "\(distance)", attributes: [.foregroundColor : UIColor.hex("F93D3D"),.font : UIFont.regularFont(12)]))
distanceFromPointAttributeString.append(NSMutableAttributedString(string: "km", attributes: [.foregroundColor : UIColor.hex("11142F"),.font : UIFont.regularFont(12)]))
distanceFromPointLabel.attributedText = distanceFromPointAttributeString
}
let leftTimeAttributeString = NSMutableAttributedString(string: "剩余时间: ",attributes: [.foregroundColor : UIColor.hex("11142F"),.font : UIFont.regularFont(12)])
if let leftTime {
leftTimeAttributeString.append(NSMutableAttributedString(string: "\(leftTime)", attributes: [.foregroundColor : UIColor.hex("F93D3D"),.font : UIFont.regularFont(12)]))
leftTimeAttributeString.append(NSMutableAttributedString(string: "", attributes: [.foregroundColor : UIColor.hex("11142F"),.font : UIFont.regularFont(12)]))
remainTimeLabel.attributedText = leftTimeAttributeString
}
let bcAttributeString = NSMutableAttributedString(string: "BC: ",attributes: [.foregroundColor : UIColor.hex("11142F"),.font : UIFont.regularFont(12)])
if let mileageBc = taskModel.mileageBc {
bcAttributeString.append(NSMutableAttributedString(string: "\(mileageBc)", attributes: [.foregroundColor : UIColor.hex("F93D3D"),.font : UIFont.regularFont(12)]))
bcAttributeString.append(NSMutableAttributedString(string: "km", attributes: [.foregroundColor : UIColor.hex("11142F"),.font : UIFont.regularFont(12)]))
distanceLabel.attributedText = bcAttributeString
}
}
func listView() -> UIView! {