|
|
@ -182,6 +182,12 @@
|
|||
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 */; };
|
||||
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 */; };
|
||||
30C4C01D2FDBF557009215C1 /* RemoveMemberViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C01C2FDBF557009215C1 /* RemoveMemberViewModel.swift */; };
|
||||
30C4C0202FDC0EC5009215C1 /* GroupInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C01F2FDC0EC5009215C1 /* GroupInfoView.swift */; };
|
||||
30C4C0222FDC0ED3009215C1 /* GroupInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C0212FDC0ED3009215C1 /* GroupInfoVC.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 */; };
|
||||
|
|
@ -408,6 +414,12 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
30C4C01A2FDBF09D009215C1 /* RemoveMemberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveMemberView.swift; sourceTree = "<group>"; };
|
||||
30C4C01C2FDBF557009215C1 /* RemoveMemberViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveMemberViewModel.swift; sourceTree = "<group>"; };
|
||||
30C4C01F2FDC0EC5009215C1 /* GroupInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupInfoView.swift; sourceTree = "<group>"; };
|
||||
30C4C0212FDC0ED3009215C1 /* GroupInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupInfoVC.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>"; };
|
||||
|
|
@ -820,9 +832,11 @@
|
|||
305A76252FCA8C7000227D26 /* GroupViewModel.swift */,
|
||||
305A76232FCA8C7000227D26 /* GroupView.swift */,
|
||||
30EFF3B82FD8FC5200EB35D4 /* VerificationPopView.swift */,
|
||||
30C4C01E2FDC0EA6009215C1 /* GroupInfo */,
|
||||
307073E42FD18A20004C37CC /* GroupChat */,
|
||||
30EFF3A22FD7C58400EB35D4 /* GroupSetting */,
|
||||
30EFF3B12FD8F19E00EB35D4 /* ReviewMemberList */,
|
||||
30C4C0172FDBF066009215C1 /* RemoveMember */,
|
||||
3062E8B82FCEAC5600CEF511 /* CreateGroup */,
|
||||
30BAB8612FCD714700C33B5C /* Join */,
|
||||
30BAB84B2FCD2FA400C33B5C /* InviteJoin */,
|
||||
|
|
@ -881,6 +895,7 @@
|
|||
30EFF3BC2FD9585200EB35D4 /* Account */,
|
||||
30EFF3D42FDA8EF200EB35D4 /* EmergencyContact */,
|
||||
30EFF3E32FDAA92200EB35D4 /* PrivacyPolicy */,
|
||||
30C4C0122FDB9178009215C1 /* CheckPermission */,
|
||||
);
|
||||
path = Mine;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -888,14 +903,14 @@
|
|||
305A763A2FCA8C7000227D26 /* Section */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
305A76262FCA8C7000227D26 /* Group */,
|
||||
305A762A2FCA8C7000227D26 /* Home */,
|
||||
305A762C2FCA8C7000227D26 /* Launch */,
|
||||
305A76312FCA8C7000227D26 /* Login */,
|
||||
305A76352FCA8C7000227D26 /* Map */,
|
||||
305A76262FCA8C7000227D26 /* Group */,
|
||||
305A76392FCA8C7000227D26 /* Mine */,
|
||||
305A798E2FCAC5F600227D26 /* InviteMember */,
|
||||
3062E8C52FCFD01000CEF511 /* VipRecharge */,
|
||||
305A76312FCA8C7000227D26 /* Login */,
|
||||
305A762C2FCA8C7000227D26 /* Launch */,
|
||||
3062E8B32FCE6BA400CEF511 /* Scan */,
|
||||
30DC18592FD11E7A0041DCD1 /* Web */,
|
||||
30EFF3AD2FD7FF1400EB35D4 /* TextInput */,
|
||||
|
|
@ -1132,6 +1147,33 @@
|
|||
path = Join;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
30C4C0122FDB9178009215C1 /* CheckPermission */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
30C4C0152FDB91B8009215C1 /* CheckPermissionVC.swift */,
|
||||
);
|
||||
path = CheckPermission;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
30C4C0172FDBF066009215C1 /* RemoveMember */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
30C4C0182FDBF094009215C1 /* RemoveMemberVC.swift */,
|
||||
30C4C01C2FDBF557009215C1 /* RemoveMemberViewModel.swift */,
|
||||
30C4C01A2FDBF09D009215C1 /* RemoveMemberView.swift */,
|
||||
);
|
||||
path = RemoveMember;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
30C4C01E2FDC0EA6009215C1 /* GroupInfo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
30C4C0212FDC0ED3009215C1 /* GroupInfoVC.swift */,
|
||||
30C4C01F2FDC0EC5009215C1 /* GroupInfoView.swift */,
|
||||
);
|
||||
path = GroupInfo;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
30DC18582FD11E7A0041DCD1 /* Controller */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -1173,8 +1215,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
30EFF3B42FD8F1D000EB35D4 /* ReviewMemberListVC.swift */,
|
||||
30EFF3B22FD8F1C200EB35D4 /* ReviewMemberListView.swift */,
|
||||
30EFF3B62FD8F86200EB35D4 /* ReviewMemberListVM.swift */,
|
||||
30EFF3B22FD8F1C200EB35D4 /* ReviewMemberListView.swift */,
|
||||
);
|
||||
path = ReviewMemberList;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1412,6 +1454,7 @@
|
|||
305A768D2FCA8C7000227D26 /* AppNetworkConfig.swift in Sources */,
|
||||
305A768E2FCA8C7000227D26 /* SignPlugin.swift in Sources */,
|
||||
305A768F2FCA8C7000227D26 /* SystemAPI.swift in Sources */,
|
||||
30C4C0222FDC0ED3009215C1 /* GroupInfoVC.swift in Sources */,
|
||||
305A76902FCA8C7000227D26 /* UserAPI.swift in Sources */,
|
||||
30EFF3DE2FDA982C00EB35D4 /* EmergencyContactAddVC.swift in Sources */,
|
||||
305A76912FCA8C7000227D26 /* Constant.swift in Sources */,
|
||||
|
|
@ -1422,6 +1465,7 @@
|
|||
305A76942FCA8C7000227D26 /* UploadImageCell.swift in Sources */,
|
||||
305A76952FCA8C7000227D26 /* CornerRadiusCell.swift in Sources */,
|
||||
305A76962FCA8C7000227D26 /* CornerRadiusFooterView.swift in Sources */,
|
||||
30C4C0162FDB91B8009215C1 /* CheckPermissionVC.swift in Sources */,
|
||||
305A76972FCA8C7000227D26 /* CornerRadiusHeaderView.swift in Sources */,
|
||||
305A76982FCA8C7000227D26 /* ImagePicker.swift in Sources */,
|
||||
305A76992FCA8C7000227D26 /* ImagePickerPopup.swift in Sources */,
|
||||
|
|
@ -1478,6 +1522,7 @@
|
|||
305A76BD2FCA8C7000227D26 /* PaginationModel.swift in Sources */,
|
||||
305A76BE2FCA8C7000227D26 /* ResponseModel.swift in Sources */,
|
||||
30EFF3C02FD958AE00EB35D4 /* AccountVC.swift in Sources */,
|
||||
30C4C0202FDC0EC5009215C1 /* GroupInfoView.swift in Sources */,
|
||||
305A76BF2FCA8C7000227D26 /* ListService.swift in Sources */,
|
||||
305A76C02FCA8C7000227D26 /* BaseNavigationController.swift in Sources */,
|
||||
305A76C12FCA8C7000227D26 /* BaseViewController.swift in Sources */,
|
||||
|
|
@ -1489,6 +1534,7 @@
|
|||
305A76C32FCA8C7000227D26 /* MainTabBarController.swift in Sources */,
|
||||
30EFF3CA2FDA575600EB35D4 /* CancellationPopView.swift in Sources */,
|
||||
305A76C42FCA8C7000227D26 /* QuickLocationTabBar.swift in Sources */,
|
||||
30C4C01D2FDBF557009215C1 /* RemoveMemberViewModel.swift in Sources */,
|
||||
305A76C52FCA8C7000227D26 /* Account.swift in Sources */,
|
||||
305A76C62FCA8C7000227D26 /* AppContextManager.swift in Sources */,
|
||||
305A76C72FCA8C7000227D26 /* UserConfigModel.swift in Sources */,
|
||||
|
|
@ -1556,6 +1602,7 @@
|
|||
305A76F02FCA8C7000227D26 /* MineViewModel.swift in Sources */,
|
||||
305A76F12FCA8C7000227D26 /* SystemService.swift in Sources */,
|
||||
30DC18522FD009CD0041DCD1 /* VipExpenseModel.swift in Sources */,
|
||||
30C4C01B2FDBF09D009215C1 /* RemoveMemberView.swift in Sources */,
|
||||
307073E12FD15F50004C37CC /* GroupIMService.swift in Sources */,
|
||||
305A76F22FCA8C7000227D26 /* UserService.swift in Sources */,
|
||||
305A76F32FCA8C7000227D26 /* AutoLayout+NSLayoutConstraint.swift in Sources */,
|
||||
|
|
@ -1575,6 +1622,7 @@
|
|||
305A76FD2FCA8C7000227D26 /* EmptyDataSetSource.swift in Sources */,
|
||||
30EFF3CD2FDA668A00EB35D4 /* MyProfileView.swift in Sources */,
|
||||
305A76FE2FCA8C7000227D26 /* EmptyDataSetView.swift in Sources */,
|
||||
30C4C0192FDBF094009215C1 /* RemoveMemberVC.swift in Sources */,
|
||||
305A76FF2FCA8C7000227D26 /* EmptyDataSetView+Extension.swift in Sources */,
|
||||
305A77002FCA8C7000227D26 /* RefreshStyle.swift in Sources */,
|
||||
305A77012FCA8C7000227D26 /* DLHUD.swift in Sources */,
|
||||
|
|
@ -1643,7 +1691,7 @@
|
|||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 434CGNSJ28;
|
||||
DEVELOPMENT_TEAM = 7LP48T8ZJE;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = "$(inherited)";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = QuickLocation/Info.plist;
|
||||
|
|
@ -1664,7 +1712,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.zuom8.jisulocation;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.zuom8.jisuloca;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
|
|
@ -1692,7 +1740,7 @@
|
|||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 434CGNSJ28;
|
||||
DEVELOPMENT_TEAM = 7LP48T8ZJE;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = "$(inherited)";
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = QuickLocation/Info.plist;
|
||||
|
|
@ -1713,7 +1761,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.zuom8.jisulocation;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.zuom8.jisuloca;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
||||
22
QuickLocation/Assets.xcassets/CheckPermission/permission.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "permission@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "permission@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
QuickLocation/Assets.xcassets/CheckPermission/permission.imageset/permission@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
QuickLocation/Assets.xcassets/CheckPermission/permission.imageset/permission@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 188 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Rectangle_281@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Rectangle_281@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
QuickLocation/Assets.xcassets/Common/checkbox_20x20.imageset/Rectangle_281@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
QuickLocation/Assets.xcassets/Common/checkbox_20x20.imageset/Rectangle_281@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Vector@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Vector@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_614@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_614@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
|
@ -33,8 +33,8 @@ class BaseViewController: UIViewController {
|
|||
/// 导航栏标题
|
||||
var navTitle: String = "" {
|
||||
didSet {
|
||||
// backButton.setTitle(" \(navTitle)", for: .normal)
|
||||
navigationItem.title = navTitle
|
||||
backButton.setTitle(" \(navTitle)", for: .normal)
|
||||
// navigationItem.title = navTitle
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ class AppContextManager: NSObject {
|
|||
var sex: Int {
|
||||
account?.sex ?? -1
|
||||
}
|
||||
/// VIP
|
||||
var vip: Int {
|
||||
account?.vip ?? 1
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func saveAccount(_ account: UserConfigModel) -> Bool {
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@ struct UserConfigModel: Mappable {
|
|||
}
|
||||
|
||||
struct SystemConfigModel: Mappable {
|
||||
/// 聊天界面的警告说明
|
||||
var chatWarning: String = ""
|
||||
|
||||
/// 圈子里的banner
|
||||
var groupBannerList: [String] = []
|
||||
|
||||
|
|
@ -66,6 +69,7 @@ struct SystemConfigModel: Mappable {
|
|||
}
|
||||
|
||||
mutating func mapping(map: Map) {
|
||||
chatWarning <- map["client.chatwarning", nested: false]
|
||||
groupBannerList <- map["client.team.ad", nested: false]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,14 @@ enum Route: String {
|
|||
case vipRights = "vipRights"
|
||||
/// 群聊
|
||||
case groupChat = "groupChat"
|
||||
/// 圈子资料
|
||||
case groupInfo = "groupInfo"
|
||||
/// 圈子设置
|
||||
case groupSetting = "groupSetting"
|
||||
/// 待审核列表
|
||||
case reviewMemberList = "reviewMemberList"
|
||||
/// 移除圈子成员
|
||||
case removeMember = "removeMember"
|
||||
/// 账号与安全
|
||||
case account = "account"
|
||||
/// 更换手机号
|
||||
|
|
@ -45,6 +51,8 @@ enum Route: String {
|
|||
case emergencyContactAdd = "emergencyContactAdd"
|
||||
/// 隐私与协议
|
||||
case privacyPolicy = "privacyPolicy"
|
||||
/// 权限检测
|
||||
case checkPermission = "checkPermission"
|
||||
}
|
||||
|
||||
extension Route: RouterTarget {
|
||||
|
|
@ -181,12 +189,30 @@ extension AppRouter: AppRouterProtocol {
|
|||
return GroupChatVC(groupId: groupId)
|
||||
}
|
||||
|
||||
// MARK: - 圈子资料
|
||||
AppRouter.register(Route.groupInfo) { url, parameters in
|
||||
let vc = GroupInfoVC(groupInfo: parameters["groupInfo"].safeDictionary as! [String : Any])
|
||||
return vc
|
||||
}
|
||||
|
||||
// MARK: - 圈子设置
|
||||
AppRouter.register(Route.groupSetting) { url, parameters in
|
||||
let groupId = parameters["groupId"].safeString
|
||||
return GroupSettingVC(groupId: groupId)
|
||||
}
|
||||
|
||||
// MARK: - 待审核列表
|
||||
AppRouter.register(Route.reviewMemberList) { url, parameters in
|
||||
let groupId = parameters["groupId"].safeString
|
||||
return ReviewMemberListVC(groupId: groupId)
|
||||
}
|
||||
|
||||
// MARK: - 移除圈子成员
|
||||
AppRouter.register(Route.removeMember) { url, parameters in
|
||||
let groupId = parameters["groupId"].safeString
|
||||
return RemoveMemberVC(groupId: groupId)
|
||||
}
|
||||
|
||||
// MARK: - 账号与安全
|
||||
AppRouter.register(Route.account) { url, parameters in
|
||||
let vc = AccountVC()
|
||||
|
|
@ -226,6 +252,11 @@ extension AppRouter: AppRouterProtocol {
|
|||
AppRouter.register(Route.privacyPolicy) { url, parameters in
|
||||
PrivacyPolicyVC()
|
||||
}
|
||||
|
||||
// MARK: - 权限检测
|
||||
AppRouter.register(Route.checkPermission) { url, parameters in
|
||||
CheckPermissionVC()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ struct GroupInfoResponse: BaseModelProtocol {
|
|||
var model: GroupInfoModel?
|
||||
// 圈子成员列表
|
||||
var list: [GroupMemberModel] = []
|
||||
// 待审核数量
|
||||
var reviewCount: Int = 0
|
||||
|
||||
init?(map: Map) {}
|
||||
|
||||
|
|
@ -62,6 +64,24 @@ struct GroupInfoResponse: BaseModelProtocol {
|
|||
message <- map["msg"]
|
||||
model <- map["data.attr"]
|
||||
list <- map["data.employee"]
|
||||
reviewCount <- map["data.reviewCount"]
|
||||
}
|
||||
}
|
||||
|
||||
struct ReviewMemberListResponse: BaseModelProtocol {
|
||||
// 状态码
|
||||
var code: String?
|
||||
// 消息
|
||||
var message: String?
|
||||
/// 待审核列表
|
||||
var list: [GroupMemberModel] = []
|
||||
|
||||
init?(map: Map) {}
|
||||
|
||||
mutating func mapping(map: Map) {
|
||||
code <- map["code"]
|
||||
message <- map["msg"]
|
||||
list <- map["data"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,6 +135,8 @@ struct GroupInfoModel: Mappable, Equatable {
|
|||
var description: String = ""
|
||||
/// 会员等级
|
||||
var level: String = ""
|
||||
/// 邀请码
|
||||
var share_code: String = ""
|
||||
|
||||
init?(map: Map) {
|
||||
|
||||
|
|
@ -129,6 +151,7 @@ struct GroupInfoModel: Mappable, Equatable {
|
|||
level <- map["level"]
|
||||
review <- map["review"]
|
||||
description <- map["description"]
|
||||
share_code <- map["share_code"]
|
||||
people_no <- (map["people_no"], kStrTransformInt)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:smartdrive.zuom8.cn</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict/>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -46,16 +46,26 @@ final class GroupChatVC: BaseViewController {
|
|||
setupMessageListener()
|
||||
setupVoiceRecording()
|
||||
setupPanelDismiss()
|
||||
setupKeyboard()
|
||||
|
||||
// 并行加载:业务接口 + IM SDK 互不依赖,同时发起
|
||||
requestGroupInfoByKey()
|
||||
viewModel.loadMessages()
|
||||
|
||||
// 处理系统配置
|
||||
guard let config = AppContextManager.shared.systemConfig else { return }
|
||||
rootView.chatWarningView.isHidden = config.chatWarning.isEmpty
|
||||
rootView.chatWarningLab.text = config.chatWarning
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
setupKeyboard()
|
||||
IQKeyboardManager.shared.isEnabled = false
|
||||
IQKeyboardManager.shared.resignOnTouchOutside = false
|
||||
|
||||
// 非圈主 会员权益拦截
|
||||
guard viewModel.groupId.contains(AppContextManager.shared.userId) == false else { return }
|
||||
rootView.disableIMView.isHidden = AppContextManager.shared.vip > 1
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
|
|
@ -65,12 +75,19 @@ final class GroupChatVC: BaseViewController {
|
|||
IQKeyboardManager.shared.resignOnTouchOutside = true
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
||||
// MARK: - Keyboard
|
||||
private func setupKeyboard() {
|
||||
// 键盘升起
|
||||
NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
|
||||
.subscribe(onNext: { [weak self] noti in
|
||||
guard let self = self,
|
||||
// 只在 VC 本身可见时响应,避免导航栈下层 VC 被全局通知唤醒
|
||||
self.isViewLoaded && self.view.window != nil,
|
||||
let userInfo = noti.userInfo,
|
||||
let frame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
|
||||
else { return }
|
||||
|
|
@ -165,6 +182,10 @@ final class GroupChatVC: BaseViewController {
|
|||
})
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
rootView.reviewBtn.rx.tap.subscribe(onNext: { _ in
|
||||
AppRouter.push(Route.reviewMemberList, userInfo: ["groupId": self.viewModel.groupId])
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
// 语音按钮
|
||||
rootView.voiceBtn.rx.tap.subscribe(onNext: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
|
|
@ -410,10 +431,10 @@ final class GroupChatVC: BaseViewController {
|
|||
GroupService.groupInfoByKey(viewModel.groupId).subscribe { response in
|
||||
guard let model = response.model else { return }
|
||||
self.viewModel.memberList = response.list
|
||||
self.viewModel.buildAvatarCache()
|
||||
self.rootView.groupNameLabel.text = model.name
|
||||
self.rootView.groupAvatarView.image = model.groupIcon
|
||||
self.rootView.reviewBtn.isHidden = !model.is_owner
|
||||
self.rootView.reviewDotView.isHidden = response.reviewCount == 0
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
|
|
@ -517,6 +538,7 @@ extension GroupChatVC: PhotoPickerControllerDelegate {
|
|||
let localMsg = ChatMessage(
|
||||
id: localId,
|
||||
isSelf: true,
|
||||
senderId: AppContextManager.shared.userId,
|
||||
avatar: viewModel.getUserAvatar(id: AppContextManager.shared.userId),
|
||||
senderName: AppContextManager.shared.name,
|
||||
content: "",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ enum VoiceRecordState {
|
|||
struct ChatMessage {
|
||||
let id: String
|
||||
let isSelf: Bool
|
||||
let senderId: String
|
||||
let avatar: UIImage
|
||||
let senderName: String
|
||||
let content: String
|
||||
|
|
@ -78,9 +79,12 @@ class GroupChatView: UIView {
|
|||
rightIconsView.addSubview(reviewBtn)
|
||||
rightIconsView.addSubview(memberBtn)
|
||||
rightIconsView.addSubview(settingBtn)
|
||||
rightIconsView.addSubview(reviewDotView)
|
||||
|
||||
addSubview(tableView)
|
||||
addSubview(chatWarningView)
|
||||
addSubview(bottomBar)
|
||||
addSubview(disableIMView)
|
||||
bottomBar.addSubview(bottomBarCornerView)
|
||||
addSubview(voiceRecordView)
|
||||
addSubview(emojiPanelView)
|
||||
|
|
@ -127,6 +131,12 @@ class GroupChatView: UIView {
|
|||
.left().centerY()
|
||||
.width(24).height(24)
|
||||
|
||||
reviewDotView.layoutChain
|
||||
.topToView(reviewBtn, offset: -2)
|
||||
.leftToRightOfView(reviewBtn, offset: -6)
|
||||
.width(8)
|
||||
.height(8)
|
||||
|
||||
memberBtn.layoutChain
|
||||
.leftToRightOfView(reviewBtn, offset: 20)
|
||||
.centerY()
|
||||
|
|
@ -143,11 +153,20 @@ class GroupChatView: UIView {
|
|||
.edgesHorzontal()
|
||||
.bottomToTopOfView(bottomBar)
|
||||
|
||||
chatWarningView.layoutChain
|
||||
.topToBottomOfView(navBarView)
|
||||
.edgesHorzontal()
|
||||
|
||||
bottomBar.layoutChain
|
||||
.edgesHorzontal(15)
|
||||
.height(50)
|
||||
.bottom(kSafeBottomMargin + 20)
|
||||
|
||||
disableIMView.layoutChain
|
||||
.edgesHorzontal(15)
|
||||
.height(50)
|
||||
.bottom(kSafeBottomMargin + 20)
|
||||
|
||||
bottomBarBottomConstraint = bottomBar.jh_constraint(.bottom, toAttribute: .bottom, otherView: bottomBar.superview, relation: .equal)
|
||||
|
||||
bottomBarCornerView.layoutChain.edges()
|
||||
|
|
@ -254,6 +273,14 @@ class GroupChatView: UIView {
|
|||
return btn
|
||||
}()
|
||||
|
||||
lazy var reviewDotView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .red
|
||||
view.cornerRadius = 4
|
||||
view.isHidden = true
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var memberBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
btn.setImage(UIImage(named: "IM/member"), for: .normal)
|
||||
|
|
@ -266,6 +293,47 @@ class GroupChatView: UIView {
|
|||
return btn
|
||||
}()
|
||||
|
||||
// MARK: - chatwarning
|
||||
lazy var chatWarningView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .black.withAlphaComponent(0.5)
|
||||
view.isHidden = true
|
||||
|
||||
view.addSubview(closeBtn)
|
||||
closeBtn.layoutChain
|
||||
.right(10)
|
||||
.height(10)
|
||||
.width(10)
|
||||
.centerY()
|
||||
|
||||
view.addSubview(chatWarningLab)
|
||||
chatWarningLab.layoutChain
|
||||
.edgesVertical(5)
|
||||
.left(15)
|
||||
.rightToLeftOfView(closeBtn, offset: -8)
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var closeBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
btn.backgroundColor = .clear
|
||||
btn.setImage(UIImage(named: "Group/close"), for: .normal)
|
||||
btn.extendEdgeInsets = UIEdgeInsets(top: 20, left: 30, bottom: 20, right: 10)
|
||||
btn.rx.tap.subscribe(onNext: { _ in
|
||||
self.chatWarningView.isHidden = true
|
||||
}).disposed(by: disposeBag)
|
||||
return btn
|
||||
}()
|
||||
|
||||
lazy var chatWarningLab: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = .white
|
||||
label.font = .systemFont(ofSize: 10, weight: .regular)
|
||||
label.numberOfLines = 0
|
||||
return label
|
||||
}()
|
||||
|
||||
// MARK: - Message List
|
||||
lazy var tableView: UITableView = {
|
||||
let tv = UITableView(frame: .zero, style: .plain)
|
||||
|
|
@ -346,6 +414,32 @@ class GroupChatView: UIView {
|
|||
return btn
|
||||
}()
|
||||
|
||||
/// IM功能禁用
|
||||
lazy var disableIMView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
view.layer.shadowColor = UIColor(hexStr: "#0F2846", alpha: 0.1).cgColor
|
||||
view.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
view.layer.shadowOpacity = 1
|
||||
view.layer.shadowRadius = 9
|
||||
view.isHidden = true
|
||||
|
||||
let v = UIView()
|
||||
v.backgroundColor = .white
|
||||
v.cornerRadius = 25
|
||||
view.addSubview(v)
|
||||
v.layoutChain.edges()
|
||||
|
||||
let label = UILabel()
|
||||
label.text = "开通VIP后可发送消息"
|
||||
label.textColor = ThemeManager.shared.color.titleAuxColor
|
||||
label.font = .systemFont(ofSize: 15, weight: .medium)
|
||||
view.addSubview(label)
|
||||
label.layoutChain.centerX().centerY()
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
/// 语音面板
|
||||
lazy var voiceRecordView: VoiceRecordView = {
|
||||
let v = VoiceRecordView()
|
||||
|
|
|
|||
|
|
@ -45,7 +45,11 @@ final class GroupChatViewModel {
|
|||
private let timeGapThreshold: TimeInterval = 300 // 5 minutes
|
||||
|
||||
var groupModel: GroupInfoModel?
|
||||
var memberList: [GroupMemberModel] = []
|
||||
var memberList: [GroupMemberModel] = [] {
|
||||
didSet {
|
||||
buildAvatarCache()
|
||||
}
|
||||
}
|
||||
var groupId: String = ""
|
||||
|
||||
// MARK: - Init
|
||||
|
|
@ -72,6 +76,43 @@ final class GroupChatViewModel {
|
|||
cache[member.user_id] = member.userIcon
|
||||
}
|
||||
avatarCache = cache
|
||||
// 刷新已有消息的头像(并行加载时 loadMessages 可能先于 memberList 完成)
|
||||
refreshMessageAvatars()
|
||||
}
|
||||
|
||||
/// 用 avatarCache 刷新已有消息的头像(并行加载时 loadMessages 可能先于 memberList 完成)
|
||||
private func refreshMessageAvatars() {
|
||||
var items = messagesSubject.value
|
||||
var didChange = false
|
||||
items = items.map { item in
|
||||
switch item {
|
||||
case var .send(m): if updateAvatar(&m) { didChange = true }; return .send(m)
|
||||
case var .received(m): if updateAvatar(&m) { didChange = true }; return .received(m)
|
||||
case var .emojiSend(m): if updateAvatar(&m) { didChange = true }; return .emojiSend(m)
|
||||
case var .emojiReceived(m): if updateAvatar(&m) { didChange = true }; return .emojiReceived(m)
|
||||
case var .voiceSend(m): if updateAvatar(&m) { didChange = true }; return .voiceSend(m)
|
||||
case var .voiceReceived(m): if updateAvatar(&m) { didChange = true }; return .voiceReceived(m)
|
||||
case var .imageSend(m): if updateAvatar(&m) { didChange = true }; return .imageSend(m)
|
||||
case var .imageReceived(m): if updateAvatar(&m) { didChange = true }; return .imageReceived(m)
|
||||
default: return item
|
||||
}
|
||||
}
|
||||
if didChange {
|
||||
messagesSubject.accept(items)
|
||||
}
|
||||
}
|
||||
|
||||
/// 尝试用缓存更新单条消息的头像,返回是否变更
|
||||
private func updateAvatar(_ msg: inout ChatMessage) -> Bool {
|
||||
guard let cached = avatarCache[msg.senderId], cached != msg.avatar else { return false }
|
||||
msg = ChatMessage(
|
||||
id: msg.id, isSelf: msg.isSelf, senderId: msg.senderId,
|
||||
avatar: cached, senderName: msg.senderName,
|
||||
content: msg.content, voiceUrl: msg.voiceUrl, imageUrl: msg.imageUrl,
|
||||
imageWidth: msg.imageWidth, imageHeight: msg.imageHeight,
|
||||
timestamp: msg.timestamp, showTime: msg.showTime, isUploading: msg.isUploading
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
func getUserAvatar(id: String) -> UIImage {
|
||||
|
|
@ -81,7 +122,7 @@ final class GroupChatViewModel {
|
|||
avatarCache[id] = image
|
||||
return image
|
||||
}
|
||||
return UIImage(named: "GroupIcon1") ?? UIImage()
|
||||
return UIImage(named: "UserIcon/1") ?? UIImage()
|
||||
}
|
||||
|
||||
func getUserNickName(id: String) -> String {
|
||||
|
|
@ -141,6 +182,8 @@ final class GroupChatViewModel {
|
|||
}
|
||||
DispatchQueue.main.async {
|
||||
self.messagesSubject.accept(items)
|
||||
// 进入会话后清除未读
|
||||
self.markAsRead()
|
||||
}
|
||||
},
|
||||
onFailure: { code, msg in
|
||||
|
|
@ -148,6 +191,14 @@ final class GroupChatViewModel {
|
|||
})
|
||||
}
|
||||
|
||||
/// 标记当前会话为已读
|
||||
private func markAsRead() {
|
||||
let conversationID = "sg_\(groupId)"
|
||||
OIMManager.manager.markConversationMessage(asRead: conversationID,
|
||||
onSuccess: nil,
|
||||
onFailure: nil)
|
||||
}
|
||||
|
||||
/// 本地消息(发送中)
|
||||
func appendLocalMessage(_ item: ChatSectionItem) {
|
||||
var items = messagesSubject.value
|
||||
|
|
@ -272,10 +323,12 @@ final class GroupChatViewModel {
|
|||
imageW = 0; imageH = 0
|
||||
}
|
||||
|
||||
let sendID = msg.sendID ?? ""
|
||||
return ChatMessage(
|
||||
id: msg.clientMsgID ?? UUID().uuidString,
|
||||
isSelf: isSelf,
|
||||
avatar: getUserAvatar(id: msg.sendID ?? ""),
|
||||
senderId: sendID,
|
||||
avatar: getUserAvatar(id: sendID),
|
||||
senderName: msg.senderNickname ?? "",
|
||||
content: content,
|
||||
voiceUrl: voiceUrl,
|
||||
|
|
@ -322,19 +375,27 @@ final class GroupChatViewModel {
|
|||
private func parseNotification(_ elem: OIMNotificationElem, contentType: Int) -> NSAttributedString? {
|
||||
guard let data = elem.detail?.data(using: .utf8),
|
||||
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||
let group = json["group"] as? [String: Any],
|
||||
let opUser = json["opUser"] as? [String: Any] else { return nil }
|
||||
let group = json["group"] as? [String: Any] else { return nil }
|
||||
|
||||
switch contentType {
|
||||
case 1501, 1510:
|
||||
// 群创建通知
|
||||
let ownerID = group["ownerUserID"] as? String ?? ""
|
||||
let ownerNickName = getUserNickName(id: ownerID)
|
||||
let text = ownerID == AppContextManager.shared.userId ? "圈子已经创建" : "\(ownerNickName) 创建了圈子"
|
||||
case 1501:
|
||||
// 群创建通知(admin 创建)
|
||||
guard let opUser = json["opUser"] as? [String: Any] else { return nil }
|
||||
let groupID = opUser["groupID"] as? String ?? ""
|
||||
let isOwner = groupID.contains(AppContextManager.shared.userId)
|
||||
let text = isOwner ? "\(AppContextManager.shared.name) 创建了圈子" : "圈子已经创建"
|
||||
return NSAttributedString(string: text)
|
||||
|
||||
case 1510:
|
||||
// 新成员加入通知
|
||||
guard let entrantUser = json["entrantUser"] as? [String: Any] else { return nil }
|
||||
let nickName = entrantUser["nickname"] as? String ?? entrantUser["userID"] as? String ?? ""
|
||||
let text = "\(nickName) 加入了圈子"
|
||||
return NSAttributedString(string: text)
|
||||
|
||||
case 1520:
|
||||
// 群名称改变通知
|
||||
guard let opUser = json["opUser"] as? [String: Any] else { return nil }
|
||||
let opUserID = opUser["userID"] as? String ?? ""
|
||||
let opNickName = getUserNickName(id: opUserID)
|
||||
let newName = group["groupName"] as? String ?? ""
|
||||
|
|
@ -343,7 +404,6 @@ final class GroupChatViewModel {
|
|||
let tip = "\(opNickName) 将群名称修改为 "
|
||||
let result = NSMutableAttributedString(string: tip + newName)
|
||||
result.addAttribute(.font, value: UIFont.systemFont(ofSize: 12), range: NSRange(location: 0, length: result.length))
|
||||
// 群名用主题蓝色
|
||||
let nameRange = NSRange(location: tip.count, length: newName.utf16.count)
|
||||
result.addAttribute(.foregroundColor, value: UIColor(hexStr: "#16B3FF"), range: nameRange)
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// GroupInfoVC.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/12.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import ObjectMapper
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
class GroupInfoVC: BaseViewController {
|
||||
|
||||
fileprivate var rootView: GroupInfoView!
|
||||
|
||||
override func loadView() {
|
||||
rootView = GroupInfoView(frame: UIScreen.main.bounds)
|
||||
view = rootView
|
||||
}
|
||||
|
||||
private let groupInModel: GroupInfoModel?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
guard let model = groupInModel else { return }
|
||||
|
||||
rootView.groupIcon.image = model.groupIcon
|
||||
rootView.groupNameLab.text = model.name
|
||||
|
||||
rootView.applyBtn.rx.tap.subscribe(onNext: { _ in
|
||||
self.requestOperateGroup()
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
private func requestOperateGroup() {
|
||||
guard let model = groupInModel else { return }
|
||||
DLToast.showLoading()
|
||||
GroupService.operate(opType: "join",
|
||||
requestData: ["share_code" : model.share_code]).subscribe(onNext: { response in
|
||||
DLToast.show(text: "申请成功")
|
||||
}, onError: { (error) in }).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
init(groupInfo: [String: Any]) {
|
||||
self.groupInModel = GroupInfoModel.init(JSON: groupInfo)
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
//
|
||||
// GroupInfoView.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/12.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
class GroupInfoView: 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(groupIcon)
|
||||
addSubview(groupNameLab)
|
||||
addSubview(applyBtn)
|
||||
|
||||
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)
|
||||
|
||||
groupIcon.layoutChain
|
||||
.topToBottomOfView(navBarView, offset: 24)
|
||||
.width(80)
|
||||
.height(80)
|
||||
.centerX()
|
||||
|
||||
groupNameLab.layoutChain
|
||||
.topToBottomOfView(groupIcon, offset: 4)
|
||||
.centerX()
|
||||
|
||||
applyBtn.layoutChain
|
||||
.edgesHorzontal(30)
|
||||
.bottom(kSafeBottomMargin + 10)
|
||||
.height(50)
|
||||
}
|
||||
|
||||
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 groupIcon: UIImageView = {
|
||||
let view = UIImageView()
|
||||
view.backgroundColor = .clear
|
||||
view.cornerRadius = 40
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var groupNameLab: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .systemFont(ofSize: 16, weight: .medium)
|
||||
label.textColor = ThemeManager.shared.color.titleAuxColor
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var applyBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
btn.setTitle("申请加入", for: .normal)
|
||||
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
||||
btn.setTitleColor(.white, for: .normal)
|
||||
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()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
|
@ -85,10 +85,22 @@ class GroupSettingVC: BaseViewController {
|
|||
AppRouter.push(Route.inviteJoin, userInfo: ["groupInfo": model.toJSON()])
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
// 审核成员
|
||||
rootView.auditMemberView.rx.tapGesture.subscribe(onNext: { _ in
|
||||
guard let model = self.viewModel.groupModel else { return }
|
||||
AppRouter.push(Route.reviewMemberList, userInfo: ["groupId": model.group_key])
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
// 移除圈子成员
|
||||
rootView.removeMemberView.rx.tapGesture.subscribe(onNext: { _ in
|
||||
guard let model = self.viewModel.groupModel else { return }
|
||||
AppRouter.push(Route.removeMember, userInfo: ["groupId": model.group_key])
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
// 解散圈子
|
||||
rootView.dismissGroupView.rx.tapGesture.subscribe(onNext: { _ in
|
||||
VerificationPopView.show {
|
||||
|
||||
self.requestDismissGroup()
|
||||
}
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
|
|
@ -165,6 +177,16 @@ class GroupSettingVC: BaseViewController {
|
|||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func requestDismissGroup() {
|
||||
DLToast.showLoading()
|
||||
GroupService.dismiss(requestData: ["group_key": viewModel.groupId]).subscribe { response in
|
||||
DLToast.dismiss()
|
||||
DLToast.show(text: "成功解散") {
|
||||
AppRouter.shared.popToRoot()
|
||||
}
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
init(groupId: String) {
|
||||
self.viewModel = GroupSettingViewModel(groupId: groupId)
|
||||
|
|
|
|||
|
|
@ -54,11 +54,10 @@ final class GroupViewController: BaseViewController {
|
|||
guard !hasSetupIMListeners else { return }
|
||||
hasSetupIMListeners = true
|
||||
|
||||
GroupIMService.shared.setConversationListener { [weak self] _ in
|
||||
GroupIMService.shared.getConversationList { conversations in
|
||||
// 监听器回调已包含完整的会话列表,直接使用,避免重复 MJExtension 解析
|
||||
GroupIMService.shared.setConversationListener { [weak self] conversations in
|
||||
self?.viewModel.updateConversations(conversations)
|
||||
}
|
||||
}
|
||||
|
||||
// 群组同步完成(如刚创建/加入的群)后刷新群列表,解决业务接口先返回、SDK 后同步的时序问题
|
||||
GroupIMService.shared.setGroupListener { [weak self] in
|
||||
|
|
@ -105,6 +104,10 @@ final class GroupViewController: BaseViewController {
|
|||
.observe(on: MainScheduler.asyncInstance)
|
||||
.bind(to: rootView.joinedTableView.rx.items(dataSource: joinedDataSource))
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
rootView.hotGroupsCollectionView.rx.modelSelected(GroupInfoModel.self)
|
||||
.subscribe(viewModel.hotGroupCellAction.inputs)
|
||||
.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func reactiveAction() {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import RxSwift
|
|||
import RxCocoa
|
||||
import RxDataSources
|
||||
import OpenIMSDK
|
||||
import ObjectMapper
|
||||
|
||||
typealias HotGroupListSectionModel = SectionModel<String, GroupInfoModel>
|
||||
typealias CircleListSectionModel = SectionModel<String, GroupCellData>
|
||||
|
|
@ -52,6 +53,13 @@ final class GroupViewModel {
|
|||
hotGroupSectionedItems.onNext(list.mapSection())
|
||||
}
|
||||
|
||||
lazy var hotGroupCellAction: Action<GroupInfoModel, Void> = { this in
|
||||
return Action { model in
|
||||
AppRouter.push(Route.groupInfo, userInfo: ["groupInfo": model.toJSON()])
|
||||
return .empty()
|
||||
}
|
||||
}(self)
|
||||
|
||||
/// 加载群列表(全量刷新)
|
||||
func loadIMGroups(_ groups: [OIMGroupInfo], conversations: [OIMConversationInfo] = []) {
|
||||
if !groups.isEmpty { cachedGroups = groups }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
//
|
||||
// RemoveMemberVC.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/12.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
import ObjectMapper
|
||||
|
||||
class RemoveMemberVC: BaseViewController {
|
||||
|
||||
fileprivate var rootView: RemoveMemberView!
|
||||
|
||||
override func loadView() {
|
||||
rootView = RemoveMemberView(frame: UIScreen.main.bounds)
|
||||
view = rootView
|
||||
}
|
||||
|
||||
private var viewModel: RemoveMemberViewModel
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
bindViewModel()
|
||||
requestGroupInfo()
|
||||
}
|
||||
|
||||
private func bindViewModel() {
|
||||
viewModel.output.sectionedItems
|
||||
.bind(to: rootView.tableView.rx.items(dataSource: dataSource))
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
viewModel.output.sectionedItems
|
||||
.subscribe(onNext: { [weak self] sections in
|
||||
let count = sections.first?.items.count ?? 0
|
||||
self?.rootView.updateHeight(itemCount: count)
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
rootView.tableView.rx.modelSelected(GroupMemberModel.self)
|
||||
.subscribe(viewModel.cellAction.inputs)
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
// selectedMembers 驱动删除按钮状态和文字
|
||||
let selectedCount = viewModel.selectedMembers
|
||||
.map { $0.count }
|
||||
.share(replay: 1)
|
||||
|
||||
selectedCount
|
||||
.map { $0 > 0 }
|
||||
.bind(to: rootView.deleteBtn.rx.isEnabled)
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
selectedCount
|
||||
.map { "删除(\($0))" }
|
||||
.bind(to: rootView.deleteBtn.rx.title())
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
rootView.deleteBtn.rx.tap.subscribe(onNext: { _ in
|
||||
self.showConfirmPop(showCloseBtn: true,
|
||||
title: "确定要移除成员吗?",
|
||||
message: "成员将从当前圈子中移除",
|
||||
confirmText: "否",
|
||||
cancelText: "是") {
|
||||
self.requestRemoveMember()
|
||||
}
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
lazy private var dataSource: RxTableViewSectionedReloadDataSource<ReviewMemberListSectionModel> = {
|
||||
return RxTableViewSectionedReloadDataSource<ReviewMemberListSectionModel>(
|
||||
configureCell: { (_, tableView, indexPath, model) in
|
||||
let cell: RemoveMemberCell = tableView.dequeueReusableCell(for: indexPath)
|
||||
cell.configure(model,
|
||||
isOwn: self.viewModel.groupId.contains(model.user_id),
|
||||
isSelected: self.viewModel.isSelected(id: model.user_id))
|
||||
return cell
|
||||
})
|
||||
}()
|
||||
|
||||
// MARK: - API
|
||||
private func requestGroupInfo() {
|
||||
DLToast.showLoading()
|
||||
GroupService.groupInfoByKey(viewModel.groupId).subscribe { response in
|
||||
DLToast.dismiss()
|
||||
guard let model = response.model else { return }
|
||||
self.viewModel.groupModel = model
|
||||
self.viewModel.loadData(response.list)
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - API删除成员
|
||||
private func requestRemoveMember() {
|
||||
DLToast.showLoading()
|
||||
GroupService.kickMember(requestData: ["group_key": viewModel.groupId,
|
||||
"kick_userid": viewModel.selectedMembers.value]).subscribe { response in
|
||||
DLToast.dismiss()
|
||||
self.viewModel.selectedMembers.accept([])
|
||||
self.requestGroupInfo()
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
init(groupId: String) {
|
||||
self.viewModel = RemoveMemberViewModel(groupId: groupId)
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
//
|
||||
// RemoveMemberView.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/12.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
class RemoveMemberView: UIView {
|
||||
|
||||
var disposeBag = DisposeBag()
|
||||
/// 单行高度
|
||||
static let rowHeight: CGFloat = 85
|
||||
|
||||
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)
|
||||
navBarView.addSubview(deleteBtn)
|
||||
|
||||
addSubview(titleLab)
|
||||
addSubview(cornerBgView)
|
||||
addSubview(tableView)
|
||||
|
||||
navBgView.layoutChain
|
||||
.edges(excludingEdge: .bottom)
|
||||
.height(kNaviHeight)
|
||||
|
||||
navBarView.layoutChain
|
||||
.edges(excludingEdge: .bottom)
|
||||
.height(kNaviHeight)
|
||||
|
||||
navTitleLabel.layoutChain
|
||||
.top(kStatusBarHeight + 12)
|
||||
.centerX()
|
||||
|
||||
backBtn.layoutChain
|
||||
.centerY(navTitleLabel)
|
||||
.left(15)
|
||||
.width(24)
|
||||
.height(24)
|
||||
|
||||
deleteBtn.layoutChain
|
||||
.right(15)
|
||||
.width(100)
|
||||
.height(44)
|
||||
.centerY(navTitleLabel)
|
||||
|
||||
titleLab.layoutChain
|
||||
.topToBottomOfView(navBarView, offset: 8)
|
||||
.left(15)
|
||||
|
||||
cornerBgView.layoutChain
|
||||
.topToBottomOfView(titleLab, offset: 16)
|
||||
.edgesHorzontal(15)
|
||||
|
||||
tableView.layoutChain
|
||||
.topToBottomOfView(titleLab, offset: 16)
|
||||
.edgesHorzontal(15)
|
||||
}
|
||||
|
||||
/// 根据数据源数量更新高度,上限到 bottom = kSafeBottomMargin + 10
|
||||
func updateHeight(itemCount: Int) {
|
||||
let contentH = CGFloat(itemCount) * Self.rowHeight
|
||||
let maxH = dl.height - kNaviHeight - kSafeBottomMargin - 10 - 16
|
||||
let targetH = min(contentH, maxH)
|
||||
|
||||
tableView.layoutChain.height(targetH)
|
||||
cornerBgView.layoutChain.height(targetH)
|
||||
tableView.isScrollEnabled = targetH > maxH
|
||||
}
|
||||
|
||||
private var listHeightConstraint: NSLayoutConstraint?
|
||||
|
||||
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 deleteBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
btn.setTitle("删除(0)", for: .normal)
|
||||
btn.setTitleColor(UIColor(hexStr: "#FF383C"), for: .normal)
|
||||
btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium)
|
||||
btn.contentHorizontalAlignment = .right
|
||||
return btn
|
||||
}()
|
||||
|
||||
lazy var titleLab: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = "圈子成员"
|
||||
label.font = .systemFont(ofSize: 16, weight: .semibold)
|
||||
label.textColor = ThemeManager.shared.color.titleAuxColor
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var cornerBgView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor(hexStr: "#F5FBFF")
|
||||
view.cornerRadius = 10
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var tableView: UITableView = {
|
||||
let tableView = UITableView(frame: .zero, style: .plain)
|
||||
tableView.backgroundColor = .clear
|
||||
tableView.separatorStyle = .none
|
||||
tableView.estimatedRowHeight = 85
|
||||
tableView.isScrollEnabled = false
|
||||
tableView.register(RemoveMemberCell.self)
|
||||
return tableView
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: .zero)
|
||||
backgroundColor = .white
|
||||
setupUI()
|
||||
setupRx()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: - RemoveMemberCell
|
||||
class RemoveMemberCell: UITableViewCell {
|
||||
|
||||
var disposeBag = DisposeBag()
|
||||
|
||||
func configure(_ model: GroupMemberModel, isOwn: Bool, isSelected: Bool) {
|
||||
avaterImgView.image = model.userIcon
|
||||
nameLab.text = model.nick_name
|
||||
ownView.isHidden = !isOwn
|
||||
selectedBtn.isHidden = isOwn
|
||||
selectedBtn.isSelected = isSelected
|
||||
}
|
||||
|
||||
override init(style: CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
backgroundColor = .clear
|
||||
setupSubviews()
|
||||
}
|
||||
|
||||
private func setupSubviews() {
|
||||
contentView.addSubview(bgView)
|
||||
bgView.addSubview(avaterImgView)
|
||||
bgView.addSubview(nameLab)
|
||||
bgView.addSubview(stackView)
|
||||
bgView.addSubview(lineView)
|
||||
|
||||
bgView.layoutChain
|
||||
.edgesHorzontal()
|
||||
.edgesVertical()
|
||||
|
||||
avaterImgView.layoutChain
|
||||
.edgesVertical(20)
|
||||
.left(15)
|
||||
.height(45)
|
||||
.widthToHeight(1)
|
||||
|
||||
nameLab.layoutChain
|
||||
.leftToRightOfView(avaterImgView, offset: 10)
|
||||
.centerY(avaterImgView)
|
||||
|
||||
stackView.layoutChain
|
||||
.right(15)
|
||||
.centerY()
|
||||
|
||||
ownView.layoutChain.width(25).height(35)
|
||||
selectedBtn.layoutChain.width(20).height(20)
|
||||
|
||||
lineView.layoutChain
|
||||
.height(0.5)
|
||||
.edgesHorzontal(15)
|
||||
.bottom()
|
||||
}
|
||||
|
||||
lazy var bgView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear//UIColor(hexStr: "#F5FBFF")
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var avaterImgView: UIImageView = {
|
||||
let view = UIImageView()
|
||||
view.backgroundColor = .clear
|
||||
view.cornerRadius = 22.5
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var nameLab: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = UIColor(hexStr: "#0F2846")
|
||||
label.font = .systemFont(ofSize: 14, weight: .medium)
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var stackView: UIStackView = {
|
||||
let view = UIStackView(arrangedSubviews: [ownView, selectedBtn])
|
||||
view.axis = .horizontal
|
||||
view.alignment = .trailing
|
||||
view.spacing = 0
|
||||
view.backgroundColor = .clear
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var ownView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
view.isHidden = true
|
||||
|
||||
let icon = UIImageView(image: UIImage(named: "Common/owner"))
|
||||
view.addSubview(icon)
|
||||
icon.layoutChain
|
||||
.top()
|
||||
.centerX()
|
||||
.width(18)
|
||||
.height(18)
|
||||
|
||||
let label = UILabel()
|
||||
label.text = "圈主"
|
||||
label.textColor = ThemeManager.shared.color.titleAuxColor
|
||||
label.font = .systemFont(ofSize: 10, weight: .medium)
|
||||
view.addSubview(label)
|
||||
label.layoutChain
|
||||
.topToBottomOfView(icon)
|
||||
.centerX()
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var selectedBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
btn.setImage(UIImage(named: "Common/checkbox_20x20"), for: .normal)
|
||||
btn.setImage(UIImage(named: "Common/delete_check"), for: .selected)
|
||||
btn.isUserInteractionEnabled = false
|
||||
btn.isHidden = true
|
||||
return btn
|
||||
}()
|
||||
|
||||
lazy var lineView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = ThemeManager.shared.color.lineColor
|
||||
return view
|
||||
}()
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
// Initialization code
|
||||
}
|
||||
|
||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
|
||||
// Configure the view for the selected state
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
disposeBag = DisposeBag()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// RemoveMemberViewModel.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/12.
|
||||
//
|
||||
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import RxDataSources
|
||||
import ObjectMapper
|
||||
|
||||
typealias RemoveMemberListSectionModel = SectionModel<String, GroupMemberModel>
|
||||
|
||||
class RemoveMemberViewModel {
|
||||
|
||||
let groupId: String
|
||||
|
||||
var groupModel: GroupInfoModel?
|
||||
|
||||
struct Output {
|
||||
var sectionedItems: Observable<[ReviewMemberListSectionModel]>
|
||||
}
|
||||
|
||||
let output: Output
|
||||
|
||||
private var disposeBag = DisposeBag()
|
||||
private let sectionedItems = PublishSubject<[ReviewMemberListSectionModel]>()
|
||||
private var list: [GroupMemberModel] = []
|
||||
|
||||
var selectedMembers = BehaviorRelay<[String]>(value: [])
|
||||
|
||||
lazy var cellAction: Action<GroupMemberModel, Void> = { this in
|
||||
return Action { model in
|
||||
let memberId = model.user_id
|
||||
guard !self.groupId.contains(memberId) else { return .empty() }
|
||||
|
||||
var current = this.selectedMembers.value
|
||||
if let idx = current.firstIndex(of: memberId) {
|
||||
current.remove(at: idx)
|
||||
} else {
|
||||
current.append(memberId)
|
||||
}
|
||||
this.selectedMembers.accept(current)
|
||||
self.sectionedItems.onNext(self.list.mapSection())
|
||||
return .empty()
|
||||
}
|
||||
}(self)
|
||||
|
||||
func isSelected(id: String) -> Bool {
|
||||
(self.selectedMembers.value.first(where: { $0 == id }) != nil)
|
||||
}
|
||||
|
||||
func loadData(_ list: [GroupMemberModel]) {
|
||||
self.list = list
|
||||
sectionedItems.onNext(list.mapSection())
|
||||
}
|
||||
|
||||
init(groupId: String) {
|
||||
self.groupId = groupId
|
||||
output = Output(
|
||||
sectionedItems: sectionedItems.asObservable()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,13 @@ class ReviewMemberListVC: BaseViewController {
|
|||
viewModel.output.sectionedItems
|
||||
.bind(to: rootView.tableView.rx.items(dataSource: dataSource))
|
||||
.disposed(by: disposeBag)
|
||||
|
||||
viewModel.output.sectionedItems
|
||||
.subscribe(onNext: { [weak self] sections in
|
||||
let count = sections.first?.items.count ?? 0
|
||||
self?.rootView.updateHeight(itemCount: count)
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
|
@ -43,6 +50,15 @@ class ReviewMemberListVC: BaseViewController {
|
|||
configureCell: { (_, tableView, indexPath, model) in
|
||||
let cell: ReviewMemberCell = tableView.dequeueReusableCell(for: indexPath)
|
||||
cell.configure(model)
|
||||
|
||||
cell.agreeBtn.rx.tap.subscribe(onNext: { _ in
|
||||
self.requestReviewMember(type: 1, userId: model.user_id)
|
||||
}).disposed(by: cell.disposeBag)
|
||||
|
||||
cell.refuseBtn.rx.tap.subscribe(onNext: { _ in
|
||||
self.requestReviewMember(type: 2, userId: model.user_id)
|
||||
}).disposed(by: cell.disposeBag)
|
||||
|
||||
return cell
|
||||
})
|
||||
}()
|
||||
|
|
@ -52,7 +68,17 @@ class ReviewMemberListVC: BaseViewController {
|
|||
DLToast.showLoading()
|
||||
GroupService.reviewlist(requestData: ["group_key": viewModel.groupId]).subscribe { response in
|
||||
DLToast.dismiss()
|
||||
self.viewModel.loadData(response.list)
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func requestReviewMember(type: Int, userId: String) {
|
||||
DLToast.showLoading()
|
||||
GroupService.reviewMember(requestData: ["group_key": viewModel.groupId,
|
||||
"review_op": type,
|
||||
"review_userid": userId]).subscribe { response in
|
||||
DLToast.dismiss()
|
||||
self.requestReviewlist()
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ struct ReviewMemberListViewModel {
|
|||
private var disposeBag = DisposeBag()
|
||||
private let sectionedItems = PublishSubject<[ReviewMemberListSectionModel]>()
|
||||
|
||||
func loadData(_ list: [GroupMemberModel]) {
|
||||
sectionedItems.onNext(list.mapSection())
|
||||
}
|
||||
|
||||
init(groupId: String) {
|
||||
self.groupId = groupId
|
||||
output = Output(
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import RxCocoa
|
|||
class ReviewMemberListView: UIView {
|
||||
|
||||
var disposeBag = DisposeBag()
|
||||
/// 单行高度
|
||||
static let rowHeight: CGFloat = 85
|
||||
|
||||
private func setupRx() {
|
||||
backBtn.rx.tap.subscribe(onNext: { _ in
|
||||
|
|
@ -30,7 +32,7 @@ class ReviewMemberListView: UIView {
|
|||
|
||||
navBgView.layoutChain
|
||||
.edges(excludingEdge: .bottom)
|
||||
.heightToWidth(160/375)
|
||||
.height(kNaviHeight)
|
||||
|
||||
navBarView.layoutChain
|
||||
.edges(excludingEdge: .bottom)
|
||||
|
|
@ -38,7 +40,6 @@ class ReviewMemberListView: UIView {
|
|||
|
||||
navTitleLabel.layoutChain
|
||||
.top(kStatusBarHeight + 12)
|
||||
.centerY(backBtn)
|
||||
.centerX()
|
||||
|
||||
backBtn.layoutChain
|
||||
|
|
@ -48,14 +49,27 @@ class ReviewMemberListView: UIView {
|
|||
.height(24)
|
||||
|
||||
cornerBgView.layoutChain
|
||||
.edges(excludingEdge: .bottom)
|
||||
.bottom(kSafeBottomMargin + 10, relation: .greaterThanOrEqual)
|
||||
.topToBottomOfView(navBarView, offset: 16)
|
||||
.edgesHorzontal(15)
|
||||
|
||||
tableView.layoutChain
|
||||
.edges(excludingEdge: .bottom)
|
||||
.bottom(kSafeBottomMargin + 10, relation: .greaterThanOrEqual)
|
||||
.topToBottomOfView(navBarView, offset: 16)
|
||||
.edgesHorzontal(15)
|
||||
}
|
||||
|
||||
/// 根据数据源数量更新高度,上限到 bottom = kSafeBottomMargin + 10
|
||||
func updateHeight(itemCount: Int) {
|
||||
let contentH = CGFloat(itemCount) * Self.rowHeight
|
||||
let maxH = dl.height - kNaviHeight - kSafeBottomMargin - 10 - 16
|
||||
let targetH = min(contentH, maxH)
|
||||
|
||||
tableView.layoutChain.height(targetH)
|
||||
cornerBgView.layoutChain.height(targetH)
|
||||
tableView.isScrollEnabled = targetH > maxH
|
||||
}
|
||||
|
||||
private var listHeightConstraint: NSLayoutConstraint?
|
||||
|
||||
lazy var navBgView: UIImageView = {
|
||||
let iv = UIImageView()
|
||||
iv.image = UIImage(named: "Common/navBar_bg_2")
|
||||
|
|
@ -96,7 +110,8 @@ class ReviewMemberListView: UIView {
|
|||
let tableView = UITableView(frame: .zero, style: .plain)
|
||||
tableView.backgroundColor = .clear
|
||||
tableView.separatorStyle = .none
|
||||
tableView.estimatedRowHeight = 77
|
||||
tableView.estimatedRowHeight = 85
|
||||
tableView.isScrollEnabled = false
|
||||
tableView.register(ReviewMemberCell.self)
|
||||
return tableView
|
||||
}()
|
||||
|
|
@ -118,8 +133,11 @@ class ReviewMemberListView: UIView {
|
|||
// MARK: - ReviewMemberCell
|
||||
class ReviewMemberCell: UITableViewCell {
|
||||
|
||||
func configure(_ model: GroupMemberModel) {
|
||||
var disposeBag = DisposeBag()
|
||||
|
||||
func configure(_ model: GroupMemberModel) {
|
||||
avaterImgView.image = model.userIcon
|
||||
nameLab.text = model.nick_name
|
||||
}
|
||||
|
||||
override init(style: CellStyle, reuseIdentifier: String?) {
|
||||
|
|
@ -135,6 +153,7 @@ class ReviewMemberCell: UITableViewCell {
|
|||
bgView.addSubview(nameLab)
|
||||
bgView.addSubview(agreeBtn)
|
||||
bgView.addSubview(refuseBtn)
|
||||
bgView.addSubview(lineView)
|
||||
|
||||
bgView.layoutChain
|
||||
.edgesHorzontal(15)
|
||||
|
|
@ -152,15 +171,20 @@ class ReviewMemberCell: UITableViewCell {
|
|||
|
||||
agreeBtn.layoutChain
|
||||
.centerY()
|
||||
.right(15)
|
||||
.width(34)
|
||||
.height(15)
|
||||
.right(5)
|
||||
.width(50)
|
||||
.height(30)
|
||||
|
||||
refuseBtn.layoutChain
|
||||
.centerY()
|
||||
.rightToLeftOfView(agreeBtn, offset: 8)
|
||||
.width(34)
|
||||
.height(15)
|
||||
.rightToLeftOfView(agreeBtn, offset: -8)
|
||||
.width(50)
|
||||
.height(30)
|
||||
|
||||
lineView.layoutChain
|
||||
.height(0.5)
|
||||
.edgesHorzontal(15)
|
||||
.bottom()
|
||||
}
|
||||
|
||||
lazy var bgView: UIView = {
|
||||
|
|
@ -187,9 +211,9 @@ class ReviewMemberCell: UITableViewCell {
|
|||
let btn = UIButton(type: .custom)
|
||||
btn.setTitle("同意", for: .normal)
|
||||
btn.setTitleColor(.white, for: .normal)
|
||||
btn.titleLabel?.font = .systemFont(ofSize: 10, weight: .regular)
|
||||
btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .regular)
|
||||
btn.setBackgroundColor(UIColor(hexStr: "#16B3FF"), for: .normal)
|
||||
btn.cornerRadius = 7.5
|
||||
btn.cornerRadius = 15
|
||||
return btn
|
||||
}()
|
||||
|
||||
|
|
@ -197,14 +221,20 @@ class ReviewMemberCell: UITableViewCell {
|
|||
let btn = UIButton(type: .custom)
|
||||
btn.setTitle("拒绝", for: .normal)
|
||||
btn.setTitleColor(UIColor(hexStr: "#16B3FF"), for: .normal)
|
||||
btn.titleLabel?.font = .systemFont(ofSize: 10, weight: .regular)
|
||||
btn.titleLabel?.font = .systemFont(ofSize: 14, weight: .regular)
|
||||
btn.setBackgroundColor(.white, for: .normal)
|
||||
btn.borderWidth = 0.5
|
||||
btn.borderColor = UIColor(hexStr: "#16B3FF")
|
||||
btn.cornerRadius = 7.5
|
||||
btn.cornerRadius = 15
|
||||
return btn
|
||||
}()
|
||||
|
||||
lazy var lineView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = ThemeManager.shared.color.lineColor
|
||||
return view
|
||||
}()
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
|
@ -219,4 +249,9 @@ class ReviewMemberCell: UITableViewCell {
|
|||
|
||||
// Configure the view for the selected state
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
disposeBag = DisposeBag()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,25 +126,31 @@ class HomeViewController: BaseViewController {
|
|||
Defaults[\.loginToken] = model.token
|
||||
AppContextManager.shared.systemConfig = model.config
|
||||
self.getUserIMToken()
|
||||
self.requestUserInfo()
|
||||
self.requestGroupInfo()
|
||||
// 先更新用户信息(含头像),再拉群列表同步地图标注,避免头像旧
|
||||
self.requestUserInfo { [weak self] in
|
||||
self?.requestGroupInfo()
|
||||
}
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
/// 获取用户IM Token
|
||||
func getUserIMToken() {
|
||||
DLToast.showLoading()
|
||||
UserService.imToken().subscribe(onNext: { response in
|
||||
guard let data = response.data, let token = data["token"] as? String else { return }
|
||||
AppContextManager.shared.imToken = token
|
||||
GroupIMService.shared.login { _ in }
|
||||
GroupIMService.shared.login { _ in
|
||||
DLToast.dismiss()
|
||||
}
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
private func requestUserInfo() {
|
||||
private func requestUserInfo(completion: (() -> Void)? = nil) {
|
||||
UserService.userInfo().subscribe { response in
|
||||
guard let model = response.model else { return }
|
||||
AppContextManager.shared.saveAccount(model)
|
||||
self.rootView.avatarImgView.image = model.userIcon
|
||||
completion?()
|
||||
}.disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ class LaunchViewController: BaseViewController {
|
|||
guard let model = response.model else { return }
|
||||
Defaults[\.loginToken] = model.token
|
||||
AppContextManager.shared.systemConfig = model.config
|
||||
// 保存用户数据
|
||||
// AppContextManager.shared.saveAccount(model)
|
||||
self.getUserIMToken()
|
||||
}, onError: { [weak self] (error) in
|
||||
DLAlert.show(title: error.localizedDescription,
|
||||
defaultTitle: "重试") { [weak self] in
|
||||
|
|
@ -82,6 +81,16 @@ class LaunchViewController: BaseViewController {
|
|||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
/// 获取用户IM Token
|
||||
func getUserIMToken() {
|
||||
DLToast.showLoading()
|
||||
UserService.imToken().subscribe(onNext: { response in
|
||||
guard let data = response.data, let token = data["token"] as? String else { return }
|
||||
AppContextManager.shared.imToken = token
|
||||
GroupIMService.shared.login { _ in }
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,172 @@
|
|||
//
|
||||
// CheckPermissionVC.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/12.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
class CheckPermissionVC: BaseViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
setupUI()
|
||||
|
||||
backBtn.rx.tap.subscribe(onNext: { _ in
|
||||
AppRouter.shared.popOrDismiss()
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
tipsLab.rx.tapGesture.subscribe { _ in
|
||||
AppRouter.push(Route.web, userInfo: ["url": URLManager.shared.privacyPolicyUrl])
|
||||
}.disposed(by: disposeBag)
|
||||
|
||||
settingBtn.rx.tap.subscribe(onNext: { _ in
|
||||
guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return }
|
||||
if UIApplication.shared.canOpenURL(settingsURL) {
|
||||
UIApplication.shared.open(settingsURL, options: [:], completionHandler: nil)
|
||||
}
|
||||
}).disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
let manager = AuthorizeManager.manager(type: .locationAlways)
|
||||
settingBtn.isEnabled = manager?.authorizeStatus() != .authorized
|
||||
}
|
||||
|
||||
private func setupUI() {
|
||||
view.addSubview(navBgView)
|
||||
view.addSubview(navBarView)
|
||||
navBarView.addSubview(navTitleLabel)
|
||||
navBarView.addSubview(backBtn)
|
||||
|
||||
view.addSubview(scrollView)
|
||||
scrollView.addSubview(scrollContentView)
|
||||
scrollContentView.addSubview(stepImgView)
|
||||
view.addSubview(tipsLab)
|
||||
view.addSubview(settingBtn)
|
||||
|
||||
navBgView.layoutChain
|
||||
.edges(excludingEdge: .bottom)
|
||||
.height(kNaviHeight)
|
||||
|
||||
navBarView.layoutChain
|
||||
.edges(excludingEdge: .bottom)
|
||||
.height(kNaviHeight)
|
||||
|
||||
navTitleLabel.layoutChain
|
||||
.top(kStatusBarHeight + 12)
|
||||
.centerX()
|
||||
|
||||
backBtn.layoutChain
|
||||
.centerY(navTitleLabel)
|
||||
.left(15)
|
||||
.width(24)
|
||||
.height(24)
|
||||
|
||||
settingBtn.layoutChain
|
||||
.edgesHorzontal(30)
|
||||
.bottom(kSafeBottomMargin + 30)
|
||||
.height(50)
|
||||
|
||||
tipsLab.layoutChain
|
||||
.edgesHorzontal(31)
|
||||
.bottomToTopOfView(settingBtn, offset: -20)
|
||||
|
||||
scrollView.layoutChain
|
||||
.topToBottomOfView(navBarView, offset: 15)
|
||||
.edgesHorzontal()
|
||||
.bottomToTopOfView(tipsLab, offset: -10)
|
||||
|
||||
scrollContentView.layoutChain
|
||||
.edges()
|
||||
.widthToView(scrollView)
|
||||
|
||||
stepImgView.layoutChain
|
||||
.top()
|
||||
.edgesHorzontal(20)
|
||||
.bottom()
|
||||
.heightToWidth(531/335)
|
||||
}
|
||||
|
||||
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 scrollView: UIScrollView = {
|
||||
let view = UIScrollView()
|
||||
view.backgroundColor = .clear
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var scrollContentView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .clear
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var stepImgView: UIImageView = {
|
||||
let view = UIImageView(image: UIImage(named: "CheckPermission/permission"))
|
||||
view.contentMode = .scaleAspectFill
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var tipsLab: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .systemFont(ofSize: 10, weight: .regular)
|
||||
label.textColor = UIColor(hexStr: "#333333")
|
||||
label.textAlignment = .center
|
||||
label.numberOfLines = 0
|
||||
label.isUserInteractionEnabled = true
|
||||
|
||||
let text = "根据我们的隐私政策和用户设置,您的位置数据也将与第三方共享,用于研究、定制广告和分析目的。"
|
||||
let attr = NSMutableAttributedString(string: text)
|
||||
let range = (text as NSString).range(of: "隐私政策")
|
||||
attr.addAttribute(.foregroundColor, value: UIColor(hexStr: "#16B3FF"), range: range)
|
||||
label.attributedText = attr
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var settingBtn: UIButton = {
|
||||
let btn = UIButton(type: .custom)
|
||||
btn.setTitle("去设置", for: .normal)
|
||||
btn.setTitle("已设置", for: .disabled)
|
||||
btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
||||
btn.setTitleColor(.white, for: .normal)
|
||||
btn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal)
|
||||
btn.cornerRadius = 25
|
||||
return btn
|
||||
}()
|
||||
|
||||
}
|
||||
|
|
@ -44,6 +44,8 @@ class MineViewModel {
|
|||
AppRouter.push(Route.privacyPolicy)
|
||||
case "在线客服":
|
||||
this.requestWechatService()
|
||||
case "权限检测":
|
||||
AppRouter.push(Route.checkPermission)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class ScanVC: BaseViewController {
|
|||
private func requestJoinGroup(code: String) {
|
||||
DLToast.showLoading()
|
||||
GroupService.operate(opType: "join", requestData: ["share_code" : code]).subscribe(onNext: { response in
|
||||
DLToast.show(text: "加入成功") {
|
||||
DLToast.show(text: "申请成功") {
|
||||
NotificationCenter.default.post(name: .RefreshGroupInfoNotification, object: nil)
|
||||
AppRouter.shared.popOrDismiss()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ class FullscreenWebView: WKWebView {
|
|||
if self.isHome {
|
||||
self.navTitle = self.webView.title ?? ""
|
||||
} else {
|
||||
self.navTitle = self.webView.title ?? ""//self.webView.title == "物联物美" ? "便民商圈" : self.webView.title ?? ""
|
||||
self.navTitle = self.webView.title ?? ""
|
||||
}
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
|
|
|
|||
|
|
@ -99,10 +99,26 @@ struct GroupService {
|
|||
/// 审核列表
|
||||
/// - Parameters:
|
||||
/// - requestData:group_key
|
||||
static func reviewlist(requestData: [String: Any]) -> Observable<GroupInfoResponse> {
|
||||
static func reviewlist(requestData: [String: Any]) -> Observable<ReviewMemberListResponse> {
|
||||
let api = GroupAPI.operate(opType: "reviewlist", requestData: requestData).multiTarget
|
||||
return APIProvider.request(token: api)
|
||||
.map(GroupInfoResponse.self)
|
||||
.map(ReviewMemberListResponse.self)
|
||||
.asObservable()
|
||||
}
|
||||
|
||||
/// 审核成员
|
||||
static func reviewMember(requestData: [String: Any]) -> Observable<ResponseModel> {
|
||||
let api = GroupAPI.operate(opType: "review", requestData: requestData).multiTarget
|
||||
return APIProvider.request(token: api)
|
||||
.map(ResponseModel.self)
|
||||
.asObservable()
|
||||
}
|
||||
|
||||
/// 踢出群聊
|
||||
static func kickMember(requestData: [String: Any]) -> Observable<ResponseModel> {
|
||||
let api = GroupAPI.operate(opType: "kick", requestData: requestData).multiTarget
|
||||
return APIProvider.request(token: api)
|
||||
.map(ResponseModel.self)
|
||||
.asObservable()
|
||||
}
|
||||
|
||||
|
|
@ -115,4 +131,12 @@ struct GroupService {
|
|||
.map(GroupInfoResponse.self)
|
||||
.asObservable()
|
||||
}
|
||||
|
||||
/// 解散群聊
|
||||
static func dismiss(requestData: [String: Any]) -> Observable<GroupInfoResponse> {
|
||||
let api = GroupAPI.operate(opType: "dismiss", requestData: requestData).multiTarget
|
||||
return APIProvider.request(token: api)
|
||||
.map(GroupInfoResponse.self)
|
||||
.asObservable()
|
||||
}
|
||||
}
|
||||
|
|
|
|||