|
|
@ -182,6 +182,9 @@
|
|||
30BAB8532FCD337C00C33B5C /* GroupService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAB8522FCD337C00C33B5C /* GroupService.swift */; };
|
||||
30BAB8632FCD716C00C33B5C /* JoinGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAB8622FCD716C00C33B5C /* JoinGroupVC.swift */; };
|
||||
30BAB8652FCD718A00C33B5C /* JoinGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BAB8642FCD718A00C33B5C /* JoinGroupView.swift */; };
|
||||
30BF300C2FED09BA00D9CB52 /* ScheduleDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BF300B2FED09BA00D9CB52 /* ScheduleDetailView.swift */; };
|
||||
30BF300E2FED09CC00D9CB52 /* ScheduleDetailVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BF300D2FED09CC00D9CB52 /* ScheduleDetailVC.swift */; };
|
||||
30BF30102FED0C8E00D9CB52 /* ScheduleDetailVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BF300F2FED0C8E00D9CB52 /* ScheduleDetailVM.swift */; };
|
||||
30C4C0162FDB91B8009215C1 /* CheckPermissionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C0152FDB91B8009215C1 /* CheckPermissionVC.swift */; };
|
||||
30C4C0192FDBF094009215C1 /* RemoveMemberVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C0182FDBF094009215C1 /* RemoveMemberVC.swift */; };
|
||||
30C4C01B2FDBF09D009215C1 /* RemoveMemberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C01A2FDBF09D009215C1 /* RemoveMemberView.swift */; };
|
||||
|
|
@ -216,6 +219,7 @@
|
|||
30D87D052FE1336300E958FD /* NavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D87D022FE1336300E958FD /* NavigationView.swift */; };
|
||||
30D891F52FE22E0600E958FD /* OrderAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D891F42FE22E0600E958FD /* OrderAPI.swift */; };
|
||||
30D891F72FE22E6E00E958FD /* OrderService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D891F62FE22E6E00E958FD /* OrderService.swift */; };
|
||||
30DA36BD2FECC5AB008D5A2C /* CreateScheduleVipPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DA36BC2FECC5AB008D5A2C /* CreateScheduleVipPopView.swift */; };
|
||||
30DC18522FD009CD0041DCD1 /* VipExpenseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DC18512FD009CD0041DCD1 /* VipExpenseModel.swift */; };
|
||||
30DC18542FD00C4A0041DCD1 /* VipRechargeVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DC18532FD00C4A0041DCD1 /* VipRechargeVM.swift */; };
|
||||
30DC185A2FD11E7A0041DCD1 /* WebOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DC18562FD11E7A0041DCD1 /* WebOperations.swift */; };
|
||||
|
|
@ -441,6 +445,9 @@
|
|||
30BAB8522FCD337C00C33B5C /* GroupService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupService.swift; sourceTree = "<group>"; };
|
||||
30BAB8622FCD716C00C33B5C /* JoinGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinGroupVC.swift; sourceTree = "<group>"; };
|
||||
30BAB8642FCD718A00C33B5C /* JoinGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinGroupView.swift; sourceTree = "<group>"; };
|
||||
30BF300B2FED09BA00D9CB52 /* ScheduleDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleDetailView.swift; sourceTree = "<group>"; };
|
||||
30BF300D2FED09CC00D9CB52 /* ScheduleDetailVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleDetailVC.swift; sourceTree = "<group>"; };
|
||||
30BF300F2FED0C8E00D9CB52 /* ScheduleDetailVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleDetailVM.swift; sourceTree = "<group>"; };
|
||||
30C4C0112FDABC8C009215C1 /* QuickLocation.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = QuickLocation.entitlements; sourceTree = "<group>"; };
|
||||
30C4C0152FDB91B8009215C1 /* CheckPermissionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckPermissionVC.swift; sourceTree = "<group>"; };
|
||||
30C4C0182FDBF094009215C1 /* RemoveMemberVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveMemberVC.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -476,6 +483,7 @@
|
|||
30D87D022FE1336300E958FD /* NavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationView.swift; sourceTree = "<group>"; };
|
||||
30D891F42FE22E0600E958FD /* OrderAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderAPI.swift; sourceTree = "<group>"; };
|
||||
30D891F62FE22E6E00E958FD /* OrderService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderService.swift; sourceTree = "<group>"; };
|
||||
30DA36BC2FECC5AB008D5A2C /* CreateScheduleVipPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateScheduleVipPopView.swift; sourceTree = "<group>"; };
|
||||
30DC18512FD009CD0041DCD1 /* VipExpenseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VipExpenseModel.swift; sourceTree = "<group>"; };
|
||||
30DC18532FD00C4A0041DCD1 /* VipRechargeVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VipRechargeVM.swift; sourceTree = "<group>"; };
|
||||
30DC18552FD11E7A0041DCD1 /* NavigationTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationTitleView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -1226,6 +1234,16 @@
|
|||
path = Join;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
30BF300A2FED09A300D9CB52 /* ScheduleDetail */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
30BF300D2FED09CC00D9CB52 /* ScheduleDetailVC.swift */,
|
||||
30BF300F2FED0C8E00D9CB52 /* ScheduleDetailVM.swift */,
|
||||
30BF300B2FED09BA00D9CB52 /* ScheduleDetailView.swift */,
|
||||
);
|
||||
path = ScheduleDetail;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
30C4C0122FDB9178009215C1 /* CheckPermission */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -1283,6 +1301,7 @@
|
|||
30D74AB92FEA37AD0050EB2C /* ScheduleModel.swift */,
|
||||
30D74ABB2FEA67CE0050EB2C /* CreateSchedule */,
|
||||
30D74BF22FEB6F5B0050EB2C /* LocationPicker */,
|
||||
30BF300A2FED09A300D9CB52 /* ScheduleDetail */,
|
||||
);
|
||||
path = Schedule;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1294,6 +1313,7 @@
|
|||
30D74D1E2FEBB09B0050EB2C /* CreateScheduleVM.swift */,
|
||||
30D74ABE2FEA67F30050EB2C /* CreateScheduleView.swift */,
|
||||
30D74AC02FEA6EEF0050EB2C /* CreateSchedulePopView.swift */,
|
||||
30DA36BC2FECC5AB008D5A2C /* CreateScheduleVipPopView.swift */,
|
||||
);
|
||||
path = CreateSchedule;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1698,6 +1718,7 @@
|
|||
305A76C02FCA8C7000227D26 /* BaseNavigationController.swift in Sources */,
|
||||
305A76C12FCA8C7000227D26 /* BaseViewController.swift in Sources */,
|
||||
3062E8C02FCED7BB00CEF511 /* GroupIconListView.swift in Sources */,
|
||||
30BF300C2FED09BA00D9CB52 /* ScheduleDetailView.swift in Sources */,
|
||||
305A76C22FCA8C7000227D26 /* BaseViewModel.swift in Sources */,
|
||||
30DC185A2FD11E7A0041DCD1 /* WebOperations.swift in Sources */,
|
||||
30DC185B2FD11E7A0041DCD1 /* NavigationTitleView.swift in Sources */,
|
||||
|
|
@ -1799,6 +1820,7 @@
|
|||
305A76F92FCA8C7000227D26 /* DLToast.swift in Sources */,
|
||||
305A76FA2FCA8C7000227D26 /* DLEmptyDataSet.swift in Sources */,
|
||||
305A76FB2FCA8C7000227D26 /* EmptyDataSet.swift in Sources */,
|
||||
30BF300E2FED09CC00D9CB52 /* ScheduleDetailVC.swift in Sources */,
|
||||
305A76FC2FCA8C7000227D26 /* EmptyDataSetDelegate.swift in Sources */,
|
||||
305A76FD2FCA8C7000227D26 /* EmptyDataSetSource.swift in Sources */,
|
||||
30D87CDB2FDFA9EE00E958FD /* MQTTService.swift in Sources */,
|
||||
|
|
@ -1816,6 +1838,8 @@
|
|||
30BAB8652FCD718A00C33B5C /* JoinGroupView.swift in Sources */,
|
||||
305A77062FCA8C7000227D26 /* MXScrollViewController.m in Sources */,
|
||||
305A77072FCA8C7000227D26 /* Helper.swift in Sources */,
|
||||
30BF30102FED0C8E00D9CB52 /* ScheduleDetailVM.swift in Sources */,
|
||||
30DA36BD2FECC5AB008D5A2C /* CreateScheduleVipPopView.swift in Sources */,
|
||||
305A77082FCA8C7000227D26 /* PageCollectionViewFlowLayout.swift in Sources */,
|
||||
3062E8C42FCFC90F00CEF511 /* CreateGroupVipPopView.swift in Sources */,
|
||||
30D74ABA2FEA37AD0050EB2C /* ScheduleModel.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ internal import Alamofire
|
|||
|
||||
/// 行程API
|
||||
enum ItineraryAPI {
|
||||
/// 查询行程
|
||||
/// 查询行程列表
|
||||
/// - Parameters:
|
||||
/// - follow: 只查看关注的行程
|
||||
/// - own: 只查看自己创建的行程
|
||||
|
|
@ -19,6 +19,23 @@ enum ItineraryAPI {
|
|||
/// - group_key: 过滤圈子查询
|
||||
case query(follow: Bool, own: Bool, history: Bool, group_key: String, page: Int)
|
||||
|
||||
/// 查询行程关注人
|
||||
/// - Parameters:
|
||||
/// - id: 行程ID
|
||||
case queryFollowList(id: String)
|
||||
|
||||
/// 设置行程
|
||||
/// - Parameters:
|
||||
/// - id: 更新时传入
|
||||
/// - group_keys: 只查看关注的行程
|
||||
/// - timestamp: 行程日期
|
||||
/// - points: 行程点
|
||||
case set(id: String, group_keys: [String], timestamp: Int64, points: [[String: Any]])
|
||||
|
||||
/// 删除
|
||||
/// - Parameters:
|
||||
/// - id: 行程ID
|
||||
case delete(id: String)
|
||||
}
|
||||
|
||||
extension ItineraryAPI: MultiTargetProtocol {
|
||||
|
|
@ -27,13 +44,21 @@ extension ItineraryAPI: MultiTargetProtocol {
|
|||
switch self {
|
||||
case .query:
|
||||
return "mapi/itinerary/route"
|
||||
case .queryFollowList:
|
||||
return "mapi/itinerary/route/follower"
|
||||
case .set:
|
||||
return "mapi/itinerary/route/set"
|
||||
case .delete:
|
||||
return "mapi/itinerary/route/delete"
|
||||
}
|
||||
}
|
||||
|
||||
var method: Moya.Method {
|
||||
switch self {
|
||||
case .query:
|
||||
case .query, .queryFollowList:
|
||||
return .get
|
||||
case .delete:
|
||||
return .delete
|
||||
default:
|
||||
return .post
|
||||
}
|
||||
|
|
@ -50,6 +75,29 @@ extension ItineraryAPI: MultiTargetProtocol {
|
|||
params["page"] = page
|
||||
params["limit"] = 20
|
||||
return .requestParameters(parameters: params, encoding: URLEncoding())
|
||||
|
||||
case let .queryFollowList(id):
|
||||
var params = Parameters()
|
||||
params["route_id"] = id
|
||||
return .requestParameters(parameters: params, encoding: URLEncoding())
|
||||
|
||||
case let .set(id, group_keys, timestamp, points):
|
||||
var params = Parameters()
|
||||
if id.isEmpty {
|
||||
params["timestamp"] = timestamp
|
||||
}
|
||||
else {
|
||||
params["id"] = id
|
||||
}
|
||||
params["group_keys"] = group_keys
|
||||
params["points"] = points
|
||||
|
||||
return .requestParameters(parameters: params, encoding: JSONEncoding())
|
||||
|
||||
case let .delete(id):
|
||||
var params = Parameters()
|
||||
params["id"] = id
|
||||
return .requestParameters(parameters: params, encoding: URLEncoding())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_2302@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_2302@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_2381@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_2381@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_2385@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_2385@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "按钮@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "按钮@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_2396@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_2396@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 429 KiB |
|
After Width: | Height: | Size: 751 KiB |
|
|
@ -55,6 +55,8 @@ enum Route: String {
|
|||
case checkPermission = "checkPermission"
|
||||
/// 创建行程
|
||||
case createSchedule = "createSchedule"
|
||||
/// 行程详情
|
||||
case scheduleDetail = "scheduleDetail"
|
||||
}
|
||||
|
||||
extension Route: RouterTarget {
|
||||
|
|
@ -262,7 +264,16 @@ extension AppRouter: AppRouterProtocol {
|
|||
|
||||
// MARK: - 创建行程
|
||||
AppRouter.register(Route.createSchedule) { url, parameters in
|
||||
CreateScheduleVC()
|
||||
let routeId = parameters["routeId"].safeString
|
||||
return CreateScheduleVC(routeId: routeId,
|
||||
scheduleJson: parameters["scheduleJson"].safeDictionary as! [String : Any])
|
||||
}
|
||||
|
||||
// MARK: - 行程详情
|
||||
AppRouter.register(Route.scheduleDetail) { url, parameters in
|
||||
let routeId = parameters["routeId"].safeString
|
||||
return ScheduleDetailVC(routeId: routeId,
|
||||
scheduleJson: parameters["scheduleJson"].safeDictionary as! [String : Any])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ import TagListView
|
|||
|
||||
// MARK: - CreateSchedulePopView
|
||||
class CreateSchedulePopView: UIView {
|
||||
|
||||
|
||||
var disposeBag = DisposeBag()
|
||||
|
||||
|
||||
func setupTagData(_ list: [GroupInfoModel]) {
|
||||
let nameArr = list.map { $0.name }
|
||||
tagListView.removeAllTags()
|
||||
|
|
@ -35,18 +35,18 @@ class CreateSchedulePopView: UIView {
|
|||
addSubview(dateView)
|
||||
addSubview(detailView)
|
||||
addSubview(scrollView)
|
||||
|
||||
|
||||
lineView.layoutChain
|
||||
.top(15)
|
||||
.centerX()
|
||||
.width(36)
|
||||
.height(4)
|
||||
|
||||
|
||||
dateView.layoutChain
|
||||
.topToBottomOfView(lineView, offset: 19)
|
||||
.edgesHorzontal()
|
||||
.height(22)
|
||||
|
||||
|
||||
detailView.layoutChain
|
||||
.topToBottomOfView(dateView, offset: 20)
|
||||
.edgesHorzontal()
|
||||
|
|
@ -56,16 +56,16 @@ class CreateSchedulePopView: UIView {
|
|||
.topToBottomOfView(detailView, offset: 15)
|
||||
.edges(excludingEdge: .top)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Views
|
||||
|
||||
|
||||
lazy var lineView: UIView = {
|
||||
let v = UIView()
|
||||
v.backgroundColor = UIColor(hexStr: "#EBEBEB")
|
||||
v.cornerRadius = 2
|
||||
return v
|
||||
}()
|
||||
|
||||
|
||||
lazy var dateView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
|
|
@ -79,7 +79,7 @@ class CreateSchedulePopView: UIView {
|
|||
dateLab.layoutChain.leftToRightOfView(titleLab).centerY()
|
||||
return view
|
||||
}()
|
||||
|
||||
|
||||
lazy var dateLab: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .systemFont(ofSize: 14, weight: .medium)
|
||||
|
|
@ -90,7 +90,7 @@ class CreateSchedulePopView: UIView {
|
|||
label.text = fmt.string(from: Date())
|
||||
return label
|
||||
}()
|
||||
|
||||
|
||||
lazy var detailView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
|
|
@ -104,7 +104,7 @@ class CreateSchedulePopView: UIView {
|
|||
addBtn.layoutChain.right(15).centerY().width(18).height(18)
|
||||
return view
|
||||
}()
|
||||
|
||||
|
||||
lazy var addBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
btn.setImage(UIImage(named: "Schedule/add"), for: .normal)
|
||||
|
|
@ -112,7 +112,7 @@ class CreateSchedulePopView: UIView {
|
|||
btn.extendEdgeInsets = UIEdgeInsets(top: 30, left: 30, bottom: 10, right: 15)
|
||||
return btn
|
||||
}()
|
||||
|
||||
|
||||
lazy var scrollView: UIScrollView = {
|
||||
let view = UIScrollView()
|
||||
view.backgroundColor = .white
|
||||
|
|
@ -145,7 +145,7 @@ class CreateSchedulePopView: UIView {
|
|||
|
||||
return view
|
||||
}()
|
||||
|
||||
|
||||
lazy var tableView: UITableView = {
|
||||
let tv = UITableView(frame: .zero, style: .plain)
|
||||
tv.backgroundColor = .clear
|
||||
|
|
@ -157,7 +157,7 @@ class CreateSchedulePopView: UIView {
|
|||
tv.register(SchedulePointCell.self)
|
||||
return tv
|
||||
}()
|
||||
|
||||
|
||||
/// 选择分享的圈子
|
||||
lazy var shareGroupView: UIView = {
|
||||
let view = UIView()
|
||||
|
|
@ -207,22 +207,19 @@ class CreateSchedulePopView: UIView {
|
|||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .white
|
||||
// 使用原生圆角避免 CAShapeLayer mask 隐式动画导致的弹跳
|
||||
layer.cornerRadius = 30
|
||||
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
layer.masksToBounds = true
|
||||
setupUI()
|
||||
}
|
||||
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
layoutIfNeeded()
|
||||
setCornerRadius(corners: [.topLeft, .topRight], withCornerRadii: CGSize(width: 30, height: 30))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - SchedulePointCell
|
||||
|
||||
class SchedulePointCell: UITableViewCell {
|
||||
|
|
@ -231,11 +228,12 @@ class SchedulePointCell: UITableViewCell {
|
|||
|
||||
var onLocationTap: (() -> Void)?
|
||||
var onTimeTap: (() -> Void)?
|
||||
var onRemarkChanged: ((String) -> Void)?
|
||||
|
||||
func configure(item: SchedulePointItem, index: Int, total: Int, onDelete: @escaping () -> Void) {
|
||||
disposeBag = DisposeBag()
|
||||
indexLabel.text = "\(index + 1)"
|
||||
locationLabel.text = item.locationName.isEmpty ? "点击选择地点" : item.locationName
|
||||
locationLabel.text = item.street.isEmpty ? "点击选择地点" : item.street
|
||||
remarkTF.text = item.remark
|
||||
|
||||
locationLabel.rx.tapGesture
|
||||
.when(.recognized)
|
||||
|
|
@ -274,6 +272,10 @@ class SchedulePointCell: UITableViewCell {
|
|||
|
||||
// MARK: - Init
|
||||
|
||||
@objc private func remarkDidChange() {
|
||||
onRemarkChanged?(remarkTF.text ?? "")
|
||||
}
|
||||
|
||||
override init(style: CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
|
|
@ -283,6 +285,11 @@ class SchedulePointCell: UITableViewCell {
|
|||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
disposeBag = DisposeBag()
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
contentView.addSubview(topDashView)
|
||||
contentView.addSubview(pointIcon)
|
||||
|
|
@ -297,7 +304,7 @@ class SchedulePointCell: UITableViewCell {
|
|||
cardCornerView.addSubview(deleteBtn)
|
||||
|
||||
topDashView.layoutChain
|
||||
.top().width(2)
|
||||
.top().width(1)
|
||||
|
||||
pointIcon.layoutChain
|
||||
.centerY()
|
||||
|
|
@ -311,7 +318,7 @@ class SchedulePointCell: UITableViewCell {
|
|||
bottomDashView.layoutChain
|
||||
.topToBottomOfView(pointIcon, offset: 10)
|
||||
.centerX(pointIcon)
|
||||
.width(2)
|
||||
.width(1)
|
||||
.bottom()
|
||||
|
||||
// 右侧卡片
|
||||
|
|
@ -349,15 +356,15 @@ class SchedulePointCell: UITableViewCell {
|
|||
.leftToView(locationLabel)
|
||||
.rightToLeftOfView(deleteBtn, offset: -10)
|
||||
.bottom(15)
|
||||
|
||||
|
||||
deleteBtn.layoutChain.centerY(remarkTF)
|
||||
}
|
||||
|
||||
// MARK: - Views
|
||||
private let topDashView: UIView = {
|
||||
let v = UIView()
|
||||
v.backgroundColor = UIColor(hexStr: "#E0E0E0")
|
||||
private let topDashView: DashLineView = {
|
||||
let v = DashLineView()
|
||||
v.isHidden = true
|
||||
v.backgroundColor = .clear
|
||||
return v
|
||||
}()
|
||||
|
||||
|
|
@ -367,10 +374,10 @@ class SchedulePointCell: UITableViewCell {
|
|||
return iv
|
||||
}()
|
||||
|
||||
private let bottomDashView: UIView = {
|
||||
let v = UIView()
|
||||
v.backgroundColor = UIColor(hexStr: "#E0E0E0")
|
||||
private let bottomDashView: DashLineView = {
|
||||
let v = DashLineView()
|
||||
v.isHidden = true
|
||||
v.backgroundColor = .clear
|
||||
return v
|
||||
}()
|
||||
|
||||
|
|
@ -438,3 +445,45 @@ class SchedulePointCell: UITableViewCell {
|
|||
indexLabel.setCornerRadius(corners: [.bottomRight], withCornerRadii: CGSize(width: 10, height: 10))
|
||||
}
|
||||
}
|
||||
|
||||
/// 虚线绘制视图(竖线)
|
||||
class DashLineView: UIView {
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
layer.sublayers?.forEach { if $0 is CAShapeLayer { $0.removeFromSuperlayer() } }
|
||||
|
||||
let shapeLayer = CAShapeLayer()
|
||||
shapeLayer.strokeColor = UIColor(hexStr: "#E0E0E0").cgColor
|
||||
shapeLayer.lineWidth = 1
|
||||
shapeLayer.lineDashPattern = [4, 4]
|
||||
shapeLayer.fillColor = nil
|
||||
|
||||
let path = UIBezierPath()
|
||||
path.move(to: CGPoint(x: bounds.midX, y: 0))
|
||||
path.addLine(to: CGPoint(x: bounds.midX, y: bounds.height))
|
||||
shapeLayer.path = path.cgPath
|
||||
|
||||
layer.addSublayer(shapeLayer)
|
||||
}
|
||||
}
|
||||
|
||||
/// 虚线绘制视图(横线)
|
||||
class HorizontalDashLineView: UIView {
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
layer.sublayers?.forEach { if $0 is CAShapeLayer { $0.removeFromSuperlayer() } }
|
||||
|
||||
let shapeLayer = CAShapeLayer()
|
||||
shapeLayer.strokeColor = UIColor(hexStr: "#E0E0E0").cgColor
|
||||
shapeLayer.lineWidth = 1
|
||||
shapeLayer.lineDashPattern = [4, 4]
|
||||
shapeLayer.fillColor = nil
|
||||
|
||||
let path = UIBezierPath()
|
||||
path.move(to: CGPoint(x: 0, y: bounds.midY))
|
||||
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.midY))
|
||||
shapeLayer.path = path.cgPath
|
||||
|
||||
layer.addSublayer(shapeLayer)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import RxGesture
|
|||
import AMapNaviKit
|
||||
import AMapSearchKit
|
||||
import TagListView
|
||||
import ObjectMapper
|
||||
#endif
|
||||
|
||||
class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
||||
|
|
@ -24,7 +25,7 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
override var isNavigationBarHidden: Bool { true }
|
||||
|
||||
fileprivate var rootView: CreateScheduleView!
|
||||
private let viewModel = CreateScheduleVM()
|
||||
private let viewModel: CreateScheduleVM
|
||||
private var popView: CreateSchedulePopView { rootView.createSchedulePopView }
|
||||
|
||||
override func loadView() {
|
||||
|
|
@ -39,8 +40,13 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
popView.tagListView.delegate = self
|
||||
setupMap()
|
||||
bindViewModel()
|
||||
|
||||
reactiveAction()
|
||||
requestGroupInfo()
|
||||
|
||||
guard let _ = viewModel.scheduModel else { return }
|
||||
rootView.navTitleLabel.text = "编辑行程"
|
||||
rootView.deleteBtn.isHidden = false
|
||||
// requestFollowList(id: model.id)
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
|
|
@ -52,12 +58,74 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
private func reactiveAction() {
|
||||
rootView.deleteBtn.rx.tap.subscribe(onNext: { _ in
|
||||
guard let model = self.viewModel.scheduModel else { return }
|
||||
self.showConfirmPop(title: "确定要删除吗?",
|
||||
message: "此条行程路线将被永久删除",
|
||||
confirmText: "删除",
|
||||
confirmBlock: {
|
||||
self.requestDelete(id: model.id)
|
||||
}, cancelText: "取消")
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
private func requestFollowList(id: String) {
|
||||
dl.showLoading()
|
||||
ItineraryService.queryFollowList(id: id).subscribe { response in
|
||||
self.dl.dismiss()
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func requestGroupInfo() {
|
||||
GroupService.groupInfo().subscribe { response in
|
||||
guard let model = response.model else { return }
|
||||
self.groupList = model.groups
|
||||
self.popView.setupTagData(model.groups)
|
||||
// 编辑模式:预选中已分享的圈子
|
||||
#if !targetEnvironment(simulator)
|
||||
if !self.viewModel.selectedGroupKeys.isEmpty {
|
||||
for (i, group) in self.groupList.enumerated() {
|
||||
guard i < self.popView.tagListView.tagViews.count,
|
||||
self.viewModel.selectedGroupKeys.contains(group.group_key) else { continue }
|
||||
self.popView.tagListView.tagViews[i].isSelected = true
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func requestSet(id: String="", points: [[String: Any]]) {
|
||||
let selectedDate = Int64(viewModel.selectedDate.value.timeIntervalSince1970 * 1000)
|
||||
dl.showLoading()
|
||||
ItineraryService.set(id: id,
|
||||
group_keys: viewModel.selectedGroupKeys,
|
||||
timestamp: selectedDate,
|
||||
points: points).subscribe(onNext: { response in
|
||||
self.dl.dismiss()
|
||||
self.dl.show(text: id.isEmpty ? "创建成功" : "更新成功") {
|
||||
AppRouter.shared.popOrDismiss()
|
||||
}
|
||||
}, onError: { error in
|
||||
guard let code = error.underlyingError?.code else { return }
|
||||
if code == 20010 { // "创建的行程数达到上限"
|
||||
CreateScheduleVipPopView.show()
|
||||
}
|
||||
else {
|
||||
self.dl.show(text: error.localizedDescription)
|
||||
}
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func requestDelete(id: String) {
|
||||
dl.showLoading()
|
||||
ItineraryService.delete(id: id).subscribe { response in
|
||||
self.dl.dismiss()
|
||||
self.dl.show(text: "删除成功") {
|
||||
AppRouter.shared.popOrDismiss()
|
||||
}
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +135,14 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
RxTableViewSectionedReloadDataSource<SchedulePointSection>(
|
||||
configureCell: { [weak self] _, tv, indexPath, item in
|
||||
let cell: SchedulePointCell = tv.dequeueReusableCell(for: indexPath)
|
||||
|
||||
cell.configure(item: item, index: indexPath.row,
|
||||
total: self?.viewModel.pointsRelay.value.count ?? 0,
|
||||
onDelete: { [weak self] in
|
||||
self?.viewModel.deletePointAt.onNext(indexPath.row)
|
||||
})
|
||||
|
||||
// 到达时间
|
||||
cell.onTimeTap = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
let picker = BRDatePickerView(pickerMode: .HM)
|
||||
|
|
@ -92,7 +168,7 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
}
|
||||
picker.show()
|
||||
}
|
||||
|
||||
// 地点
|
||||
cell.onLocationTap = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
let coord = item.latitude != 0 || item.longitude != 0
|
||||
|
|
@ -110,21 +186,34 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
)
|
||||
}
|
||||
vc.onPickedLocation = { picked in
|
||||
self.viewModel.updatePointLocation(index: indexPath.row, name: picked.name, address: picked.address)
|
||||
// 补充坐标
|
||||
var list = self.viewModel.pointsRelay.value
|
||||
guard indexPath.row < list.count else { return }
|
||||
list[indexPath.row].locationName = picked.name
|
||||
list[indexPath.row].address = picked.address
|
||||
list[indexPath.row].latitude = picked.coordinate.latitude
|
||||
list[indexPath.row].longitude = picked.coordinate.longitude
|
||||
list[indexPath.row].province = picked.province
|
||||
list[indexPath.row].city = picked.city
|
||||
list[indexPath.row].district = picked.district
|
||||
list[indexPath.row].street = picked.street
|
||||
list[indexPath.row].country = picked.country
|
||||
list[indexPath.row].formatted_address = picked.formatted_address
|
||||
self.viewModel.pointsRelay.accept(list)
|
||||
}
|
||||
self.present(vc, animated: true)
|
||||
}
|
||||
cell.configure(item: item, index: indexPath.row,
|
||||
total: self?.viewModel.pointsRelay.value.count ?? 0,
|
||||
onDelete: { [weak self] in
|
||||
self?.viewModel.deletePointAt.onNext(indexPath.row)
|
||||
})
|
||||
|
||||
// 备注
|
||||
cell.remarkTF.rx.controlEvent(.editingDidEnd)
|
||||
.subscribe(onNext: {
|
||||
guard let self = self, let text = cell.remarkTF.text else { return }
|
||||
var list = self.viewModel.pointsRelay.value
|
||||
guard indexPath.row < list.count else { return }
|
||||
list[indexPath.row].remark = text
|
||||
self.viewModel.pointsRelay.accept(list)
|
||||
})
|
||||
.disposed(by: cell.disposeBag)
|
||||
|
||||
return cell
|
||||
})
|
||||
}()
|
||||
|
|
@ -184,9 +273,6 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
guard hasTime.count == points.count else { DLToast.show(text: "请为每个行程点选择到达时间"); return }
|
||||
guard !viewModel.selectedGroupKeys.isEmpty else { DLToast.show(text: "请选择分享的圈子"); return }
|
||||
|
||||
// 时间戳(毫秒)
|
||||
let ts = Int64(viewModel.selectedDate.value.timeIntervalSince1970 * 1000)
|
||||
|
||||
// 生成 points 数组
|
||||
let pointsJSON: [[String: Any]] = points.map { p in
|
||||
let expectedTs = p.expectedTime.map { Int64($0.timeIntervalSince1970 * 1000) } ?? 0
|
||||
|
|
@ -205,13 +291,7 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
]
|
||||
}
|
||||
|
||||
let params: [String: Any] = [
|
||||
"group_keys": viewModel.selectedGroupKeys,
|
||||
"timestamp": ts,
|
||||
"points": pointsJSON
|
||||
]
|
||||
print("📋 Create schedule: \(params)")
|
||||
DLToast.show(text: "创建成功")
|
||||
requestSet(id: viewModel.scheduModel?.id ?? "", points: pointsJSON)
|
||||
}
|
||||
|
||||
private func setupMap() {
|
||||
|
|
@ -362,6 +442,17 @@ class CreateScheduleVC: BaseViewController, MAMapViewDelegate {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
init(routeId: String, scheduleJson: [String: Any]) {
|
||||
let model = ScheduleModel.init(JSON: scheduleJson)
|
||||
self.viewModel = CreateScheduleVM(scheduleJson.isEmpty ? nil : model)
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TagListViewDelegate
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ class CreateScheduleVM {
|
|||
|
||||
let disposeBag = DisposeBag()
|
||||
|
||||
let scheduModel: ScheduleModel?
|
||||
|
||||
// MARK: - Input
|
||||
let addPointTapped = PublishSubject<Void>()
|
||||
let deletePointAt = PublishSubject<Int>()
|
||||
|
|
@ -57,10 +59,17 @@ class CreateScheduleVM {
|
|||
let pointsRelay: BehaviorRelay<[SchedulePointItem]>
|
||||
let dateString: Observable<String>
|
||||
|
||||
init() {
|
||||
pointsRelay = BehaviorRelay<[SchedulePointItem]>(value: [
|
||||
SchedulePointItem()
|
||||
])
|
||||
init(_ model: ScheduleModel?) {
|
||||
self.scheduModel = model
|
||||
|
||||
// 编辑模式:从 model 回显数据
|
||||
if let m = model, !m.points.isEmpty {
|
||||
pointsRelay = BehaviorRelay<[SchedulePointItem]>(value: m.points.map { $0.toSchedulePointItem() })
|
||||
selectedDate.accept(Date(timeIntervalSince1970: TimeInterval(m.timestamp) / 1000))
|
||||
selectedGroupKeys = m.groups.map { $0.group_key }
|
||||
} else {
|
||||
pointsRelay = BehaviorRelay<[SchedulePointItem]>(value: [SchedulePointItem()])
|
||||
}
|
||||
|
||||
dateString = selectedDate
|
||||
.map {
|
||||
|
|
@ -103,12 +112,4 @@ class CreateScheduleVM {
|
|||
.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
/// 从 LocationPicker 回调更新某个点的位置
|
||||
func updatePointLocation(index: Int, name: String, address: String) {
|
||||
var list = pointsRelay.value
|
||||
guard index < list.count else { return }
|
||||
list[index].locationName = name
|
||||
list[index].address = address
|
||||
pointsRelay.accept(list)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,14 +31,8 @@ class CreateScheduleView: UIView {
|
|||
AppRouter.shared.popOrDismiss()
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
// scrollView 到达顶部后,由 pan 手势接管 PopView 滑动
|
||||
createSchedulePopView.scrollView.rx.contentOffset
|
||||
.observe(on: MainScheduler.asyncInstance)
|
||||
.subscribe(onNext: { [weak self] offset in
|
||||
guard let self = self, self.isSubCanScroll, offset.y <= 0 else { return }
|
||||
self.createSchedulePopView.scrollView.setContentOffset(.zero, animated: false)
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
// 嵌套滑动协调(参考 GroupView 的 PanScrollView 模式)
|
||||
createSchedulePopView.scrollView.delegate = self
|
||||
}
|
||||
|
||||
private func setupUI() {
|
||||
|
|
@ -49,6 +43,7 @@ class CreateScheduleView: UIView {
|
|||
addSubview(navBarView)
|
||||
navBarView.addSubview(backBtn)
|
||||
navBarView.addSubview(navTitleLabel)
|
||||
navBarView.addSubview(deleteBtn)
|
||||
addSubview(createSchedulePopView)
|
||||
|
||||
navBgView.layoutChain
|
||||
|
|
@ -68,6 +63,12 @@ class CreateScheduleView: UIView {
|
|||
.top(kStatusBarHeight + 12)
|
||||
.centerX()
|
||||
|
||||
deleteBtn.layoutChain
|
||||
.centerY(navTitleLabel)
|
||||
.right(15)
|
||||
.width(16)
|
||||
.height(16)
|
||||
|
||||
#if !targetEnvironment(simulator)
|
||||
mapView.layoutChain
|
||||
.top()
|
||||
|
|
@ -102,14 +103,24 @@ class CreateScheduleView: UIView {
|
|||
|
||||
case .changed:
|
||||
let newTop = panStartTop + pan.translation(in: self).y
|
||||
let scrollOffset = createSchedulePopView.scrollView.contentOffset.y
|
||||
|
||||
if isSubCanScroll {
|
||||
let scrollOffset = self.createSchedulePopView.scrollView.contentOffset.y
|
||||
// 内容未到顶部时,不干涉 scrollView 的滑动
|
||||
// 内容正在滑动,不移动 PopView
|
||||
if scrollOffset > 0 { return }
|
||||
// 到顶部后切为 view 拖拽
|
||||
isSubCanScroll = false
|
||||
panStartTop = createSchedulePopView.frame.minY
|
||||
// 内容滑到顶部,切回 Pan 拖拽(用户下拉时 velocity > 0)
|
||||
if pan.velocity(in: self).y > 0 || createSchedulePopView.frame.minY > popUpLimit + 1 {
|
||||
isSubCanScroll = false
|
||||
panStartTop = createSchedulePopView.frame.minY
|
||||
}
|
||||
} else {
|
||||
// PopView 在顶部且继续上滑 → 激活内容滑动
|
||||
if createSchedulePopView.frame.minY <= popUpLimit && newTop <= popUpLimit {
|
||||
isSubCanScroll = true
|
||||
panStartTop = createSchedulePopView.frame.minY
|
||||
topConstraint.constant = popUpLimit
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let clamped = max(popUpLimit, min(popDownLimit, newTop))
|
||||
|
|
@ -119,23 +130,32 @@ class CreateScheduleView: UIView {
|
|||
let velocity = pan.velocity(in: self)
|
||||
let isNearUp = abs(createSchedulePopView.frame.minY - popUpLimit) < abs(createSchedulePopView.frame.minY - popDownLimit)
|
||||
let target: CGFloat
|
||||
if abs(velocity.y) > 200 {
|
||||
if createSchedulePopView.frame.minY <= popUpLimit + 5 {
|
||||
// 顶部附近:由位置决定去向,速度不触发回收(避免 scrollView 松手误回收)
|
||||
target = isNearUp ? popUpLimit : popDownLimit
|
||||
} else if abs(velocity.y) > 200 {
|
||||
target = velocity.y < 0 ? popUpLimit : popDownLimit
|
||||
} else {
|
||||
target = isNearUp ? popUpLimit : popDownLimit
|
||||
}
|
||||
topConstraint.constant = target
|
||||
|
||||
// 非最大化时禁用 scollView 滚动
|
||||
// 动画前设置 scrollView 状态,避免弹跳
|
||||
let atTop = target == self.popUpLimit
|
||||
createSchedulePopView.scrollView.isScrollEnabled = atTop
|
||||
if !atTop {
|
||||
isSubCanScroll = false
|
||||
createSchedulePopView.scrollView.isScrollEnabled = false
|
||||
createSchedulePopView.scrollView.setContentOffset(.zero, animated: false)
|
||||
}
|
||||
isSubCanScroll = atTop
|
||||
|
||||
UIView.animate(withDuration: 0.2, delay: 0,
|
||||
options: [.curveEaseInOut, .allowUserInteraction]) {
|
||||
UIView.animate(withDuration: 0.35, delay: 0,
|
||||
usingSpringWithDamping: 0.85,
|
||||
initialSpringVelocity: abs(velocity.y) / 1000,
|
||||
options: [.allowUserInteraction]) {
|
||||
self.layoutIfNeeded()
|
||||
} completion: { _ in
|
||||
self.createSchedulePopView.scrollView.isScrollEnabled = atTop
|
||||
}
|
||||
|
||||
default:
|
||||
|
|
@ -181,6 +201,14 @@ class CreateScheduleView: UIView {
|
|||
label.text = "创建行程"
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var deleteBtn: UIButton = {
|
||||
let btn = UIButton()
|
||||
btn.setImage(UIImage(named: "Common/delete"), for: .normal)
|
||||
btn.extendEdgeInsets = UIEdgeInsets(top: 15, left: 20, bottom: 15, right: 15)
|
||||
btn.isHidden = true
|
||||
return btn
|
||||
}()
|
||||
|
||||
#if !targetEnvironment(simulator)
|
||||
lazy var mapView: MAMapView! = {
|
||||
|
|
@ -215,7 +243,7 @@ class CreateScheduleView: UIView {
|
|||
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
// MARK: - UIScrollViewDelegate(参考 GroupView 嵌套滑动协调)
|
||||
extension CreateScheduleView: UIScrollViewDelegate {
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
// 内容已有偏移时才允许子滚动
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
//
|
||||
// CreateScheduleVipPopView.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
class CreateScheduleVipPopView: UIView {
|
||||
|
||||
private static let shared = CreateScheduleVipPopView(frame: CGRect(origin: .zero, size: kScreenSize))
|
||||
|
||||
var disposeBag = DisposeBag()
|
||||
|
||||
static func show() {
|
||||
guard let superView = kKeyWindow else {
|
||||
return
|
||||
}
|
||||
|
||||
if CreateScheduleVipPopView.shared.superview != nil {
|
||||
CreateScheduleVipPopView.shared.removeFromSuperview()
|
||||
CreateScheduleVipPopView.shared.bgView.frame = .zero
|
||||
}
|
||||
CreateScheduleVipPopView.shared.bgView.alpha = 1
|
||||
CreateScheduleVipPopView.shared.bgView.frame = CGRect(x: 0, y: 0, width: kScreenWidth, height: kScreenHeight)
|
||||
superView.addSubview(CreateScheduleVipPopView.shared)
|
||||
superView.bringSubviewToFront(CreateScheduleVipPopView.shared)
|
||||
|
||||
UIView.animate(withDuration: 0.25) {
|
||||
CreateScheduleVipPopView.shared.bgView.alpha = 1
|
||||
}
|
||||
}
|
||||
|
||||
/// 关闭
|
||||
static func dismiss() {
|
||||
guard CreateScheduleVipPopView.shared.superview != nil else { return }
|
||||
|
||||
UIView.animate(withDuration: 0.25, delay: 0, options: [.curveEaseIn]) {
|
||||
CreateScheduleVipPopView.shared.bgView.alpha = 0
|
||||
} completion: { _ in
|
||||
CreateScheduleVipPopView.shared.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
private func setupRx() {
|
||||
upgradedBtn.rx.tap.subscribe(onNext: { _ in
|
||||
CreateScheduleVipPopView.dismiss()
|
||||
AppRouter.push(Route.vipRecharge)
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
closeBtn.rx.tap.subscribe(onNext: { _ in
|
||||
CreateScheduleVipPopView.dismiss()
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private lazy var bgView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .black.withAlphaComponent(0.5)
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var vipImgView: UIImageView = {
|
||||
let view = UIImageView()
|
||||
view.image = UIImage(named: "Schedule/vip_pop")
|
||||
view.contentMode = .scaleAspectFill
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var upgradedBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
btn.backgroundColor = .clear
|
||||
btn.setBackgroundImage(UIImage(named: "Schedule/upgrade_bg"), for: .normal)
|
||||
return btn
|
||||
}()
|
||||
|
||||
lazy var closeBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
btn.backgroundColor = .clear
|
||||
btn.setBackgroundImage(UIImage(named: "Group/close"), for: .normal)
|
||||
btn.extendEdgeInsets = UIEdgeInsets(top: 10, left: 100, bottom: 100, right: 100)
|
||||
return btn
|
||||
}()
|
||||
|
||||
// MARK: - Init
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .clear
|
||||
addSubview(bgView)
|
||||
bgView.addSubview(vipImgView)
|
||||
bgView.addSubview(upgradedBtn)
|
||||
bgView.addSubview(closeBtn)
|
||||
|
||||
vipImgView.layoutChain
|
||||
.centerY()
|
||||
.edgesHorzontal(25)
|
||||
.heightToWidth(814/648)
|
||||
|
||||
upgradedBtn.layoutChain
|
||||
.topToBottomOfView(vipImgView, offset: -20)
|
||||
.centerX()
|
||||
.width(240)
|
||||
.height(60)
|
||||
|
||||
closeBtn.layoutChain
|
||||
.topToBottomOfView(upgradedBtn, offset: 15)
|
||||
.centerX()
|
||||
.width(22)
|
||||
.height(22)
|
||||
|
||||
setupRx()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -182,6 +182,7 @@ class LocationPickerVC: BaseViewController {
|
|||
province: poi.province ?? "",
|
||||
city: poi.city ?? "",
|
||||
district: poi.district ?? "",
|
||||
street: "",
|
||||
formatted_address: poi.address ?? ""
|
||||
)
|
||||
rootView.resultTableView.isHidden = true
|
||||
|
|
@ -191,6 +192,14 @@ class LocationPickerVC: BaseViewController {
|
|||
rootView.mapView.setCenter(coord, animated: true)
|
||||
rootView.mapView.setZoomLevel(19, animated: true)
|
||||
addLocationAnnotation(coordinate: coord)
|
||||
// 逆地理编码补充 street
|
||||
#if !targetEnvironment(simulator)
|
||||
let regeo = AMapReGeocodeSearchRequest()
|
||||
regeo.location = AMapGeoPoint.location(withLatitude: CGFloat(coord.latitude),
|
||||
longitude: CGFloat(coord.longitude))
|
||||
regeo.requireExtension = true
|
||||
searchAPI?.aMapReGoecodeSearch(regeo)
|
||||
#endif
|
||||
}
|
||||
|
||||
private func updateTableHeight(itemCount: Int) {
|
||||
|
|
@ -252,11 +261,23 @@ extension LocationPickerVC: AMapSearchDelegate {
|
|||
func onReGeocodeSearchDone(_ request: AMapReGeocodeSearchRequest!, response: AMapReGeocodeSearchResponse!) {
|
||||
guard let regeo = response.regeocode else { return }
|
||||
let address = regeo.formattedAddress ?? ""
|
||||
// 取第一个 POI 名称作为地点名,没有则取街道/区
|
||||
let poiName = regeo.pois?.first?.name
|
||||
?? regeo.pois?.first?.address
|
||||
?? regeo.pois?.first?.district
|
||||
?? ""
|
||||
selectedLocation = PickedLocation(
|
||||
name: rootView.poiNameLab.text ?? "",
|
||||
address: address,
|
||||
coordinate: selectedLocation?.coordinate ?? kCLLocationCoordinate2DInvalid
|
||||
name: poiName,
|
||||
address: regeo.pois?.first?.address ?? "",
|
||||
coordinate: selectedLocation?.coordinate ?? kCLLocationCoordinate2DInvalid,
|
||||
province: regeo.pois?.first?.province ?? "",
|
||||
city: regeo.pois?.first?.city ?? "",
|
||||
district: regeo.pois?.first?.district ?? "",
|
||||
street: regeo.addressComponent?.streetNumber?.street ?? "",
|
||||
formatted_address: regeo.pois?.first?.address ?? ""
|
||||
)
|
||||
rootView.bottomView.isHidden = false
|
||||
rootView.poiNameLab.text = poiName
|
||||
rootView.poiAddressLab.text = address
|
||||
}
|
||||
|
||||
|
|
@ -267,13 +288,25 @@ extension LocationPickerVC: AMapSearchDelegate {
|
|||
|
||||
// MARK: - MAMapViewDelegate
|
||||
extension LocationPickerVC: MAMapViewDelegate {
|
||||
func mapView(_ mapView: MAMapView!, didTouchPois pois: [Any]!) {
|
||||
guard let touchPoi = pois?.first as? MATouchPoi, let uid = touchPoi.uid, !uid.isEmpty else { return }
|
||||
let request = AMapPOIIDSearchRequest()
|
||||
request.uid = uid
|
||||
searchAPI?.aMapPOIIDSearch(request)
|
||||
func mapView(_ mapView: MAMapView!, didSingleTappedAt coordinate: CLLocationCoordinate2D) {
|
||||
isShowPoi = true
|
||||
selectedLocation = PickedLocation(
|
||||
name: "",
|
||||
address: "",
|
||||
coordinate: coordinate
|
||||
)
|
||||
addLocationAnnotation(coordinate: coordinate)
|
||||
rootView.mapView.setCenter(coordinate, animated: true)
|
||||
// 逆地理编码获取地址
|
||||
#if !targetEnvironment(simulator)
|
||||
let regeo = AMapReGeocodeSearchRequest()
|
||||
regeo.location = AMapGeoPoint.location(withLatitude: CGFloat(coordinate.latitude),
|
||||
longitude: CGFloat(coordinate.longitude))
|
||||
regeo.requireExtension = true
|
||||
searchAPI?.aMapReGoecodeSearch(regeo)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
func mapView(_ mapView: MAMapView!, viewFor annotation: MAAnnotation!) -> MAAnnotationView! {
|
||||
if annotation is MAUserLocation { return nil }
|
||||
guard annotation is MAPointAnnotation else { return nil }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// ScheduleDetailVC.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
import ObjectMapper
|
||||
|
||||
class ScheduleDetailVC: BaseViewController {
|
||||
|
||||
fileprivate var rootView: ScheduleDetailView!
|
||||
|
||||
override func loadView() {
|
||||
rootView = ScheduleDetailView(frame: UIScreen.main.bounds)
|
||||
view = rootView
|
||||
}
|
||||
|
||||
private var viewModel: ScheduleDetailViewModel
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
setupData()
|
||||
bindViewModel()
|
||||
requestFollowList()
|
||||
|
||||
viewModel.loadPointData()
|
||||
}
|
||||
|
||||
private func bindViewModel() {
|
||||
viewModel.output.sectionedItems
|
||||
.bind(to: rootView.collectionView.rx.items(dataSource: dataSource))
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
viewModel.output.pointSectionedItems
|
||||
.bind(to: rootView.tableView.rx.items(dataSource: tableViewDataSource))
|
||||
.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func setupData() {
|
||||
guard let model = viewModel.scheduModel else { return }
|
||||
rootView.dateLab.text = rootView.dateLab.getDateInterval2String(date: "\(model.timestamp / 1000)", dateFormat: "yyyy年MM月dd日")
|
||||
rootView.creatorIcon.image = model.userIcon
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
private func requestFollowList() {
|
||||
dl.showLoading()
|
||||
UserService.followList().subscribe(onNext: { response in
|
||||
self.dl.dismiss()
|
||||
self.viewModel.loadViewedData(response.list)
|
||||
self.rootView.noDataLab.isHidden = response.list.count > 0
|
||||
}, onError: { _ in }).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - dataSource
|
||||
private lazy var dataSource: RxCollectionViewSectionedReloadDataSource<ViewedListSectionModel> = {
|
||||
RxCollectionViewSectionedReloadDataSource<ViewedListSectionModel> { datasource, collectionView, indexPath, model in
|
||||
let cell: ViewedCell = collectionView.dequeueReusableCell(for: indexPath)
|
||||
cell.configure(model)
|
||||
return cell
|
||||
}
|
||||
}()
|
||||
|
||||
lazy private var tableViewDataSource: RxTableViewSectionedReloadDataSource<SchedulePointDetailSectionModel> = {
|
||||
return RxTableViewSectionedReloadDataSource<SchedulePointDetailSectionModel>(
|
||||
configureCell: { (dataSource, tableView, indexPath, item) in
|
||||
switch item {
|
||||
case let .point(model):
|
||||
let cell: SchedulePointDetailCell = tableView.dequeueReusableCell(for: indexPath)
|
||||
cell.configure(model: model, index: indexPath.section, total: dataSource.sectionModels.count)
|
||||
return cell
|
||||
case let .event(model):
|
||||
let cell: SchedulePointEventCell = tableView.dequeueReusableCell(for: indexPath)
|
||||
cell.configure(model: model, index: indexPath.section, total: dataSource.sectionModels.count)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
)
|
||||
}()
|
||||
|
||||
// MARK: - Init
|
||||
init(routeId: String, scheduleJson: [String: Any]) {
|
||||
self.viewModel = ScheduleDetailViewModel(routeId: routeId,
|
||||
model: scheduleJson.isEmpty ? nil : ScheduleModel.init(JSON: scheduleJson))
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// ScheduleDetailVM.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/25.
|
||||
//
|
||||
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
|
||||
enum SchedulePointDetailItem {
|
||||
case point(model: SchedulePointModel)
|
||||
case event(model: SchedulePointEventModel)
|
||||
}
|
||||
|
||||
typealias SchedulePointDetailSectionModel = SectionModel<String, SchedulePointDetailItem>
|
||||
|
||||
struct ScheduleDetailViewModel {
|
||||
|
||||
struct Output {
|
||||
var sectionedItems: Observable<[ViewedListSectionModel]>
|
||||
var pointSectionedItems: Observable<[SchedulePointDetailSectionModel]>
|
||||
}
|
||||
|
||||
let output: Output
|
||||
|
||||
private let sectionedItems = PublishSubject<[ViewedListSectionModel]>()
|
||||
private let pointSectionedItems = PublishSubject<[SchedulePointDetailSectionModel]>()
|
||||
|
||||
let routeId: String
|
||||
let scheduModel: ScheduleModel?
|
||||
|
||||
func loadViewedData(_ list: [ViewedModel]) {
|
||||
sectionedItems.onNext(list.mapSection())
|
||||
}
|
||||
|
||||
func loadPointData() {
|
||||
guard let model = scheduModel else { return }
|
||||
|
||||
var sectionModels: [SchedulePointDetailSectionModel] = []
|
||||
var items: [SchedulePointDetailItem] = []
|
||||
|
||||
for point in model.points {
|
||||
items.append(.point(model: point))
|
||||
for event in point.events {
|
||||
items.append(.event(model: event))
|
||||
}
|
||||
sectionModels.append(SchedulePointDetailSectionModel(model: point.id, items: items))
|
||||
items = []
|
||||
}
|
||||
|
||||
pointSectionedItems.onNext(sectionModels)
|
||||
}
|
||||
|
||||
// MARK: - init
|
||||
init(routeId: String, model: ScheduleModel?) {
|
||||
self.routeId = routeId
|
||||
self.scheduModel = model
|
||||
output = Output(
|
||||
sectionedItems: sectionedItems.asObservable(),
|
||||
pointSectionedItems: pointSectionedItems.asObservable()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,534 @@
|
|||
//
|
||||
// ScheduleDetailView.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
class ScheduleDetailView: UIView {
|
||||
|
||||
var disposeBag = DisposeBag()
|
||||
|
||||
private func setupRx() {
|
||||
backBtn.rx.tap.subscribe(onNext: { _ in
|
||||
AppRouter.shared.popOrDismiss()
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func setupUI() {
|
||||
addSubview(navBgView)
|
||||
addSubview(navBarView)
|
||||
navBarView.addSubview(navTitleLabel)
|
||||
navBarView.addSubview(backBtn)
|
||||
addSubview(headerView)
|
||||
addSubview(travelRouteView)
|
||||
addSubview(tableView)
|
||||
addSubview(bottomView)
|
||||
|
||||
navBgView.layoutChain
|
||||
.edges(excludingEdge: .bottom)
|
||||
.heightToWidth(160/375)
|
||||
|
||||
navBarView.layoutChain
|
||||
.edges(excludingEdge: .bottom)
|
||||
.height(kNaviHeight)
|
||||
|
||||
navTitleLabel.layoutChain
|
||||
.top(kStatusBarHeight + 12)
|
||||
.centerY(backBtn)
|
||||
.centerX()
|
||||
|
||||
backBtn.layoutChain
|
||||
.centerY(navTitleLabel)
|
||||
.left(15)
|
||||
.width(24)
|
||||
.height(24)
|
||||
|
||||
headerView.layoutChain
|
||||
.topToBottomOfView(navBarView)
|
||||
.edgesHorzontal()
|
||||
|
||||
travelRouteView.layoutChain
|
||||
.topToBottomOfView(headerView)
|
||||
.edgesHorzontal()
|
||||
|
||||
bottomView.layoutChain
|
||||
.edgesHorzontal()
|
||||
.height(kSafeBottomMargin + 80)
|
||||
.bottom()
|
||||
|
||||
tableView.layoutChain
|
||||
.topToBottomOfView(travelRouteView, offset: 15)
|
||||
.edgesHorzontal()
|
||||
.bottomToTopOfView(bottomView, offset: 0)
|
||||
}
|
||||
|
||||
lazy var navBgView: UIImageView = {
|
||||
let iv = UIImageView()
|
||||
iv.image = UIImage(named: "Common/navBar_bg_2")
|
||||
iv.contentMode = .scaleAspectFill
|
||||
return iv
|
||||
}()
|
||||
|
||||
lazy var navBarView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var navTitleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = "行程详情"
|
||||
label.font = .systemFont(ofSize: 18, weight: .medium)
|
||||
label.textColor = ThemeManager.shared.color.titleAuxColor
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var backBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
btn.setImage(UIImage(named: "Common/back"), for: .normal)
|
||||
btn.extendEdgeInsets = UIEdgeInsets(top: 54, left: 15, bottom: 100, right: 100)
|
||||
return btn
|
||||
}()
|
||||
|
||||
/// 谁在关注
|
||||
lazy var headerView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
|
||||
let titleLab = UILabel()
|
||||
titleLab.text = "谁在关注"
|
||||
titleLab.font = .systemFont(ofSize: 16, weight: .medium)
|
||||
titleLab.textColor = ThemeManager.shared.color.titleAuxColor
|
||||
view.addSubview(titleLab)
|
||||
titleLab.layoutChain
|
||||
.top(15)
|
||||
|
||||
let dotView = UIView()
|
||||
dotView.backgroundColor = UIColor(hexStr: "#16B3FF")
|
||||
dotView.cornerRadius = 2
|
||||
view.addSubview(dotView)
|
||||
dotView.layoutChain
|
||||
.left(15)
|
||||
.centerY(titleLab)
|
||||
.width(4)
|
||||
.height(11)
|
||||
|
||||
titleLab.layoutChain.leftToRightOfView(dotView, offset: 5)
|
||||
|
||||
view.addSubview(collectionView)
|
||||
collectionView.layoutChain
|
||||
.topToBottomOfView(titleLab, offset: 15)
|
||||
.edgesHorzontal()
|
||||
.height(80)
|
||||
.bottom(15)
|
||||
|
||||
view.addSubview(noDataLab)
|
||||
noDataLab.layoutChain
|
||||
.centerX().centerY()
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var collectionView: UICollectionView = {
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
layout.scrollDirection = .horizontal
|
||||
layout.itemSize = CGSize(width: 80, height: 80)
|
||||
layout.minimumLineSpacing = 15
|
||||
layout.sectionInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
|
||||
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||
cv.backgroundColor = .clear
|
||||
cv.showsHorizontalScrollIndicator = false
|
||||
cv.register(ViewedCell.self)
|
||||
return cv
|
||||
}()
|
||||
|
||||
lazy var noDataLab: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = " 暂无关注"
|
||||
label.textColor = UIColor(hexStr: "#999999")
|
||||
label.font = .systemFont(ofSize: 14, weight: .regular)
|
||||
label.isHidden = true
|
||||
return label
|
||||
}()
|
||||
|
||||
/// 行程日期
|
||||
lazy var travelRouteView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
|
||||
let titleLab = UILabel()
|
||||
titleLab.text = "行程日期"
|
||||
titleLab.font = .systemFont(ofSize: 16, weight: .medium)
|
||||
titleLab.textColor = ThemeManager.shared.color.titleAuxColor
|
||||
view.addSubview(titleLab)
|
||||
titleLab.layoutChain
|
||||
.top(5)
|
||||
|
||||
let dotView = UIView()
|
||||
dotView.backgroundColor = UIColor(hexStr: "#16B3FF")
|
||||
dotView.cornerRadius = 2
|
||||
view.addSubview(dotView)
|
||||
dotView.layoutChain
|
||||
.left(15)
|
||||
.centerY(titleLab)
|
||||
.width(4)
|
||||
.height(11)
|
||||
|
||||
titleLab.layoutChain.leftToRightOfView(dotView, offset: 5)
|
||||
|
||||
view.addSubview(dateLab)
|
||||
dateLab.layoutChain
|
||||
.centerY(titleLab)
|
||||
.leftToRightOfView(titleLab, offset: 10)
|
||||
|
||||
view.addSubview(creatorIcon)
|
||||
creatorIcon.layoutChain
|
||||
.right(15)
|
||||
.width(30)
|
||||
.height(30)
|
||||
.centerY(titleLab)
|
||||
|
||||
let creatorTitleLab = UILabel()
|
||||
creatorTitleLab.text = "创建人"
|
||||
creatorTitleLab.font = .systemFont(ofSize: 12, weight: .medium)
|
||||
creatorTitleLab.textColor = ThemeManager.shared.color.titleAuxColor
|
||||
view.addSubview(creatorTitleLab)
|
||||
creatorTitleLab.layoutChain
|
||||
.rightToLeftOfView(creatorIcon, offset: -5)
|
||||
.centerY(titleLab)
|
||||
|
||||
view.addSubview(vipTipsLab)
|
||||
vipTipsLab.layoutChain
|
||||
.topToBottomOfView(titleLab, offset: 5)
|
||||
.leftToView(titleLab)
|
||||
.bottom()
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var dateLab: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = UIColor(hexStr: "#16B3FF")
|
||||
label.font = .systemFont(ofSize: 14, weight: .medium)
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var creatorIcon: UIImageView = {
|
||||
let view = UIImageView()
|
||||
view.cornerRadius = 15
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var vipTipsLab: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = UIColor(hexStr: "#FF7D52")
|
||||
label.font = .systemFont(ofSize: 10, weight: .regular)
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var tableView: UITableView = {
|
||||
let tv = UITableView(frame: .zero, style: .plain)
|
||||
tv.backgroundColor = .clear
|
||||
tv.separatorStyle = .none
|
||||
tv.estimatedRowHeight = 77
|
||||
tv.showsVerticalScrollIndicator = false
|
||||
tv.bounces = false
|
||||
tv.register(SchedulePointDetailCell.self)
|
||||
tv.register(SchedulePointEventCell.self)
|
||||
tv.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
|
||||
return tv
|
||||
}()
|
||||
|
||||
lazy var bottomView: UIView = {
|
||||
let v = UIView()
|
||||
v.backgroundColor = .white
|
||||
v.layer.cornerRadius = 16
|
||||
v.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
v.layer.shadowColor = UIColor.black.withAlphaComponent(0.1).cgColor
|
||||
v.layer.shadowOffset = CGSize(width: 0, height: -2)
|
||||
v.layer.shadowRadius = 10
|
||||
v.layer.shadowOpacity = 1
|
||||
return v
|
||||
}()
|
||||
|
||||
lazy var operateBtn: UIButton = {
|
||||
let btn = UIButton()
|
||||
btn.setTitleColor(.white, for: .normal)
|
||||
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
||||
btn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
|
||||
btn.cornerRadius = 25
|
||||
return btn
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
backgroundColor = .white
|
||||
setupUI()
|
||||
setupRx()
|
||||
|
||||
vipTipsLab.text = AppContextManager.shared.vip > 1 ? "" : "升级 VIP,查看具体事件"
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SchedulePointDetailCell
|
||||
class SchedulePointDetailCell: UITableViewCell {
|
||||
|
||||
var disposeBag = DisposeBag()
|
||||
|
||||
func configure(model: SchedulePointModel, index: Int, total: Int) {
|
||||
indexLabel.text = "\(index + 1)"
|
||||
locationLabel.text = model.street
|
||||
remarkLab.text = model.remark.isEmpty ? "备注:无备注" : model.remark
|
||||
timeLabel.text = getDateInterval2String(date: "\(model.expected_timestamp / 1000)", dateFormat: "HH:mm")
|
||||
|
||||
var indexName = ""
|
||||
if index == 0 {
|
||||
indexName = "起点:"
|
||||
}
|
||||
else if index == total - 1 {
|
||||
indexName = "终点:"
|
||||
}
|
||||
else {
|
||||
indexName = "途经点:"
|
||||
}
|
||||
indexNameLab.text = indexName
|
||||
bottomDashView.isHidden = index == total - 1
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
override init(style: CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
backgroundColor = .clear
|
||||
setupViews()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
disposeBag = DisposeBag()
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
contentView.addSubview(pointIcon)
|
||||
contentView.addSubview(bottomDashView)
|
||||
contentView.addSubview(indexLabel)
|
||||
contentView.addSubview(indexNameLab)
|
||||
contentView.addSubview(locationLabel)
|
||||
contentView.addSubview(timeLabel)
|
||||
contentView.addSubview(remarkLab)
|
||||
|
||||
pointIcon.layoutChain
|
||||
.top()
|
||||
.left(15)
|
||||
.width(18).height(18)
|
||||
|
||||
indexLabel.layoutChain
|
||||
.top().leftToRightOfView(pointIcon, offset: 10)
|
||||
.width(22).height(20)
|
||||
|
||||
indexNameLab.layoutChain
|
||||
.leftToRightOfView(indexLabel, offset: 5)
|
||||
.centerY(indexLabel)
|
||||
|
||||
locationLabel.layoutChain
|
||||
.centerY(indexLabel)
|
||||
.leftToRightOfView(indexNameLab, offset: 2)
|
||||
.compressionHorizontal(.defaultLow)
|
||||
|
||||
timeLabel.layoutChain
|
||||
.centerY(indexLabel)
|
||||
.leftToRightOfView(locationLabel, offset: 20)
|
||||
.right(10, relation: .greaterThanOrEqual)
|
||||
.compressionHorizontal(.required)
|
||||
|
||||
remarkLab.layoutChain
|
||||
.topToBottomOfView(indexLabel, offset: 5)
|
||||
.leftToView(indexLabel)
|
||||
.right(15)
|
||||
.bottom(10)
|
||||
.compressionVertical(.required)
|
||||
|
||||
bottomDashView.layoutChain
|
||||
.topToBottomOfView(pointIcon, offset: 5)
|
||||
.centerX(pointIcon)
|
||||
.width(0.5)
|
||||
.bottom(5)
|
||||
}
|
||||
|
||||
// MARK: - Views
|
||||
private let pointIcon: UIImageView = {
|
||||
let iv = UIImageView(image: UIImage(named: "Schedule/point"))
|
||||
iv.contentMode = .scaleAspectFit
|
||||
return iv
|
||||
}()
|
||||
|
||||
private let bottomDashView: DashLineView = {
|
||||
let v = DashLineView()
|
||||
v.backgroundColor = .clear
|
||||
return v
|
||||
}()
|
||||
|
||||
private let indexLabel: UILabel = {
|
||||
let l = UILabel()
|
||||
l.backgroundColor = UIColor(hexStr: "#DCF4FF")
|
||||
l.textColor = UIColor(hexStr: "#176F9B")
|
||||
l.font = .systemFont(ofSize: 12, weight: .medium)
|
||||
l.textAlignment = .center
|
||||
return l
|
||||
}()
|
||||
|
||||
lazy var indexNameLab: UILabel = {
|
||||
let l = UILabel()
|
||||
l.font = .systemFont(ofSize: 14, weight: .medium)
|
||||
l.textColor = UIColor(hexStr: "#16B3FF")
|
||||
return l
|
||||
}()
|
||||
|
||||
private let locationLabel: UILabel = {
|
||||
let l = UILabel()
|
||||
l.font = .systemFont(ofSize: 14, weight: .medium)
|
||||
l.textColor = UIColor(hexStr: "#333333")
|
||||
// l.numberOfLines = 0
|
||||
return l
|
||||
}()
|
||||
|
||||
private let timeLabel: UILabel = {
|
||||
let l = UILabel()
|
||||
l.font = .systemFont(ofSize: 12, weight: .medium)
|
||||
l.textColor = UIColor(hexStr: "#333333")
|
||||
l.isUserInteractionEnabled = true
|
||||
l.textAlignment = .right
|
||||
return l
|
||||
}()
|
||||
|
||||
lazy var remarkLab: UILabel = {
|
||||
let l = UILabel()
|
||||
l.font = .systemFont(ofSize: 12, weight: .regular)
|
||||
l.textColor = UIColor(hexStr: "#999999")
|
||||
l.numberOfLines = 0
|
||||
return l
|
||||
}()
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
contentView.layoutIfNeeded()
|
||||
indexLabel.setCornerRadius(corners: [.bottomRight, .topLeft], withCornerRadii: CGSize(width: 10, height: 10))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SchedulePointEventCell
|
||||
class SchedulePointEventCell: UITableViewCell {
|
||||
|
||||
var disposeBag = DisposeBag()
|
||||
|
||||
func configure(model: SchedulePointEventModel, index: Int, total: Int) {
|
||||
var suffix = ""
|
||||
if index == 0 {
|
||||
suffix = " 起点"
|
||||
} else if index == total - 1 {
|
||||
suffix = " 终点"
|
||||
} else {
|
||||
suffix = " 途经点"
|
||||
}
|
||||
infoLab.text = model.text + "到达" + suffix
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
override init(style: CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
backgroundColor = .clear
|
||||
setupViews()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
disposeBag = DisposeBag()
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
contentView.addSubview(verticalDashLineView)
|
||||
contentView.addSubview(horizontalDashLineView)
|
||||
contentView.addSubview(dotView)
|
||||
contentView.addSubview(infoView)
|
||||
infoView.addSubview(infoLab)
|
||||
|
||||
verticalDashLineView.layoutChain
|
||||
.left(24)
|
||||
.edgesVertical()
|
||||
.width(1)
|
||||
|
||||
dotView.layoutChain
|
||||
.centerX(verticalDashLineView)
|
||||
.centerY()
|
||||
.width(8)
|
||||
.height(8)
|
||||
|
||||
horizontalDashLineView.layoutChain
|
||||
.leftToRightOfView(dotView)
|
||||
.centerY(dotView)
|
||||
.height(1)
|
||||
.width(30)
|
||||
|
||||
infoView.layoutChain
|
||||
.leftToRightOfView(horizontalDashLineView, offset: 5)
|
||||
.edgesVertical(10)
|
||||
.right(15)
|
||||
|
||||
infoLab.layoutChain
|
||||
.edgesHorzontal(15)
|
||||
.edgesVertical(15)
|
||||
}
|
||||
|
||||
// MARK: - Views
|
||||
lazy var verticalDashLineView: DashLineView = {
|
||||
let v = DashLineView()
|
||||
v.backgroundColor = .clear
|
||||
return v
|
||||
}()
|
||||
|
||||
lazy var horizontalDashLineView: HorizontalDashLineView = {
|
||||
let v = HorizontalDashLineView()
|
||||
v.backgroundColor = .clear
|
||||
return v
|
||||
}()
|
||||
|
||||
lazy var dotView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor(hexStr: "#16B3FF")
|
||||
view.cornerRadius = 4
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var infoView: UIView = {
|
||||
let v = UIView()
|
||||
v.backgroundColor = .white
|
||||
v.layer.cornerRadius = 10
|
||||
v.layer.shadowColor = UIColor.black.withAlphaComponent(0.1).cgColor
|
||||
v.layer.shadowOffset = CGSize(width: 0, height: -2)
|
||||
v.layer.shadowRadius = 10
|
||||
v.layer.shadowOpacity = 1
|
||||
return v
|
||||
}()
|
||||
|
||||
lazy var infoLab: UILabel = {
|
||||
let l = UILabel()
|
||||
l.font = .systemFont(ofSize: 12, weight: .medium)
|
||||
l.textColor = UIColor(hexStr: "#333333")
|
||||
return l
|
||||
}()
|
||||
}
|
||||
|
|
@ -64,10 +64,8 @@ struct ScheduleModel: Mappable, Equatable {
|
|||
var is_own: Bool = false
|
||||
/// 行程点
|
||||
var points: [SchedulePointModel] = []
|
||||
/// 圈子名
|
||||
// var group_name: String = ""
|
||||
/// 圈子key
|
||||
// var group_key: String = ""
|
||||
/// 圈子
|
||||
var groups: [ScheduleGroupModel] = []
|
||||
|
||||
init?(map: Map) {
|
||||
|
||||
|
|
@ -82,8 +80,7 @@ struct ScheduleModel: Mappable, Equatable {
|
|||
is_follow <- map["is_follow"]
|
||||
is_own <- map["is_own"]
|
||||
points <- map["points"]
|
||||
// group_name <- map["groups.group_name"]
|
||||
// group_key <- map["groups.group_key"]
|
||||
groups <- map["groups"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +115,7 @@ struct SchedulePointModel: Mappable, Equatable {
|
|||
/// 备注
|
||||
var remark: String = ""
|
||||
/// 事件
|
||||
var eventText: String = ""
|
||||
var events: [SchedulePointEventModel] = []
|
||||
/// 节点顺序
|
||||
var sequence: Int = 0
|
||||
|
||||
|
|
@ -133,7 +130,7 @@ struct SchedulePointModel: Mappable, Equatable {
|
|||
longitude <- map["location.longitude"]
|
||||
latitude <- map["location.latitude"]
|
||||
remark <- map["remark"]
|
||||
eventText <- map["events.text"]
|
||||
events <- map["events"]
|
||||
province <- map["address.province"]
|
||||
district <- map["address.district"]
|
||||
country <- map["address.country"]
|
||||
|
|
@ -146,8 +143,89 @@ struct SchedulePointModel: Mappable, Equatable {
|
|||
|
||||
extension SchedulePointModel: IdentifiableType {
|
||||
public typealias Identity = String
|
||||
|
||||
|
||||
public var identity: String {
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
extension SchedulePointModel {
|
||||
/// 转为创建行程用的 SchedulePointItem
|
||||
func toSchedulePointItem() -> SchedulePointItem {
|
||||
var item = SchedulePointItem()
|
||||
item.locationName = formatted_address
|
||||
item.address = formatted_address
|
||||
item.latitude = latitude ?? 0
|
||||
item.longitude = longitude ?? 0
|
||||
item.province = province
|
||||
item.city = city
|
||||
item.district = district
|
||||
item.street = street
|
||||
item.country = country
|
||||
item.formatted_address = formatted_address
|
||||
if expected_timestamp > 0 {
|
||||
item.expectedTime = Date(timeIntervalSince1970: TimeInterval(expected_timestamp) / 1000)
|
||||
}
|
||||
item.remark = remark
|
||||
return item
|
||||
}
|
||||
}
|
||||
|
||||
/// 行程点事件
|
||||
struct SchedulePointEventModel: Mappable, Equatable {
|
||||
var uuid: String = UUID().uuidString
|
||||
/// 事件内容
|
||||
var text: String = ""
|
||||
/// 到达时间
|
||||
var arrival_timestamp: Int64 = 0
|
||||
///
|
||||
var user_id: String = ""
|
||||
var nick_name: String = ""
|
||||
var point_id: String = ""
|
||||
|
||||
init?(map: Map) {
|
||||
|
||||
}
|
||||
|
||||
mutating func mapping(map: Map) {
|
||||
text <- map["text"]
|
||||
arrival_timestamp <- map["arrival_timestamp"]
|
||||
user_id <- map[user_id]
|
||||
nick_name <- map["nick_name"]
|
||||
point_id <- map["point_id"]
|
||||
}
|
||||
}
|
||||
|
||||
extension SchedulePointEventModel: IdentifiableType {
|
||||
public typealias Identity = String
|
||||
|
||||
public var identity: String {
|
||||
return uuid
|
||||
}
|
||||
}
|
||||
|
||||
/// 行程分享的圈子
|
||||
struct ScheduleGroupModel: Mappable, Equatable {
|
||||
var uuid: String = UUID().uuidString
|
||||
/// id
|
||||
var group_key: String = ""
|
||||
/// 圈子名
|
||||
var group_name: String = ""
|
||||
|
||||
init?(map: Map) {
|
||||
|
||||
}
|
||||
|
||||
mutating func mapping(map: Map) {
|
||||
group_key <- map["group_key"]
|
||||
group_name <- map["group_name"]
|
||||
}
|
||||
}
|
||||
|
||||
extension ScheduleGroupModel: IdentifiableType {
|
||||
public typealias Identity = String
|
||||
|
||||
public var identity: String {
|
||||
return uuid
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import RxSwift
|
|||
import RxCocoa
|
||||
import RxDataSources
|
||||
import MJRefresh
|
||||
import ObjectMapper
|
||||
|
||||
class ScheduleVC: BaseViewController {
|
||||
|
||||
|
|
@ -30,10 +31,14 @@ class ScheduleVC: BaseViewController {
|
|||
bindViewModel()
|
||||
reactiveAction()
|
||||
|
||||
requestData()
|
||||
requestFollowList()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
requestData()
|
||||
}
|
||||
|
||||
private func bindViewModel() {
|
||||
viewModel.output.sectionedItems
|
||||
.bind(to: rootView.collectionView.rx.items(dataSource: dataSource))
|
||||
|
|
@ -48,6 +53,10 @@ class ScheduleVC: BaseViewController {
|
|||
self.dl.dismiss()
|
||||
self.rootView.tableView.refresh(status: status, isEmpty: isEmpty)
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
rootView.tableView.rx.modelSelected(ScheduleModel.self)
|
||||
.subscribe(viewModel.cellAction.inputs)
|
||||
.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func reactiveAction() {
|
||||
|
|
@ -98,6 +107,17 @@ class ScheduleVC: BaseViewController {
|
|||
configureCell: { (_, tableView, indexPath, model) in
|
||||
let cell: ScheduleListPopCell = tableView.dequeueReusableCell(for: indexPath)
|
||||
cell.configure(model)
|
||||
|
||||
// 编辑
|
||||
cell.editBtn.rx.tap.subscribe(onNext: { _ in
|
||||
AppRouter.push(Route.createSchedule, userInfo: ["scheduleJson": model.toJSON()])
|
||||
}).disposed(by: cell.disposeBag)
|
||||
|
||||
// 关注
|
||||
cell.followBtn.rx.tap.subscribe(onNext: { _ in
|
||||
|
||||
}).disposed(by: cell.disposeBag)
|
||||
|
||||
return cell
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -175,7 +175,9 @@ class ScheduleView: UIView {
|
|||
tableView.backgroundColor = .clear
|
||||
tableView.separatorStyle = .none
|
||||
tableView.estimatedRowHeight = 80
|
||||
tableView.showsVerticalScrollIndicator = false
|
||||
tableView.register(ScheduleListPopCell.self)
|
||||
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 97 + kSafeBottomMargin, right: 0)
|
||||
return tableView
|
||||
}()
|
||||
|
||||
|
|
@ -222,7 +224,7 @@ final class ViewedCell: UICollectionViewCell {
|
|||
}()
|
||||
|
||||
lazy var blurView: UIVisualEffectView = {
|
||||
let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialLight)
|
||||
let blurEffect = UIBlurEffect(style: .systemUltraThinMaterial)
|
||||
let view = UIVisualEffectView(effect: blurEffect)
|
||||
return view
|
||||
}()
|
||||
|
|
@ -270,11 +272,14 @@ final class ViewedCell: UICollectionViewCell {
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: - ScheduleListPopCell
|
||||
class ScheduleListPopCell: UITableViewCell {
|
||||
|
||||
var disposeBag = DisposeBag()
|
||||
|
||||
func configure(_ model: ScheduleModel) {
|
||||
nameLab.text = model.nick_name + " 的行程路线"
|
||||
monthLab.text = getDateInterval2String(date: "\(model.timestamp/1000)", dateFormat: "MM月")
|
||||
|
|
@ -355,6 +360,11 @@ class ScheduleListPopCell: UITableViewCell {
|
|||
// Configure the view for the selected state
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
disposeBag = DisposeBag()
|
||||
}
|
||||
|
||||
lazy var bgView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import RxSwift
|
|||
import RxCocoa
|
||||
import RxDataSources
|
||||
import MJRefresh
|
||||
import ObjectMapper
|
||||
|
||||
typealias ViewedListSectionModel = SectionModel<String, ViewedModel>
|
||||
typealias ScheduleListSectionModel = AnimatableSectionModel<String, ScheduleModel>
|
||||
|
|
@ -47,6 +48,13 @@ class ScheduleViewModel: ViewModelType {
|
|||
sectionedItems.onNext(list.mapSection())
|
||||
}
|
||||
|
||||
lazy var cellAction: Action<ScheduleModel, Void> = { this in
|
||||
return Action { model in
|
||||
AppRouter.push(Route.scheduleDetail, userInfo: ["scheduleJson": model.toJSON()])
|
||||
return .empty()
|
||||
}
|
||||
}(self)
|
||||
|
||||
// MARK: - init
|
||||
init() {
|
||||
listService = ItineraryService.query()
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import Moya
|
|||
struct ItineraryService {
|
||||
static let disposeBag = DisposeBag()
|
||||
|
||||
/// 查询行程
|
||||
/// 查询行程列表
|
||||
/// - Parameters:
|
||||
/// - follow: 只查看关注的行程
|
||||
/// - own: 只查看自己创建的行程
|
||||
|
|
@ -30,4 +30,36 @@ struct ItineraryService {
|
|||
}
|
||||
}
|
||||
|
||||
/// 查询行程关注人
|
||||
/// - Parameters:
|
||||
/// - id: 行程ID
|
||||
static func queryFollowList(id: String) -> Observable<ResponseModel> {
|
||||
let api = ItineraryAPI.queryFollowList(id: id).multiTarget
|
||||
return APIProvider.request(token: api)
|
||||
.map(ResponseModel.self)
|
||||
.asObservable()
|
||||
}
|
||||
|
||||
/// 设置行程
|
||||
/// - Parameters:
|
||||
/// - id: 更新时传入
|
||||
/// - group_keys: 只查看关注的行程
|
||||
/// - timestamp: 行程日期
|
||||
/// - points: 行程点
|
||||
static func set(id: String="", group_keys: [String], timestamp: Int64, points: [[String: Any]]) -> Observable<ResponseModel> {
|
||||
let api = ItineraryAPI.set(id: id, group_keys: group_keys, timestamp: timestamp, points: points).multiTarget
|
||||
return APIProvider.request(token: api)
|
||||
.map(ResponseModel.self)
|
||||
.asObservable()
|
||||
}
|
||||
|
||||
/// 删除
|
||||
/// - Parameters:
|
||||
/// - id: 行程ID
|
||||
static func delete(id: String) -> Observable<ResponseModel> {
|
||||
let api = ItineraryAPI.delete(id: id).multiTarget
|
||||
return APIProvider.request(token: api)
|
||||
.map(ResponseModel.self)
|
||||
.asObservable()
|
||||
}
|
||||
}
|
||||
|
|
|
|||