Commit b59f60ee501f9d4c2c8b0d9990a6ffd2507474ce
1 parent
47a2e13e
排序判断逻辑
Showing
9 changed files
with
213 additions
and
68 deletions
metaCode/Classes/Config/CNLiveConfigUserInfo.swift
| ... | ... | @@ -109,10 +109,123 @@ class UserInfoManager: NSObject { |
| 109 | 109 | static public func clearUserInfo() { |
| 110 | 110 | UserDefaults.standard.removeObject(forKey: "mateCode_userInfo") |
| 111 | 111 | UserInfoManager.shared._userInfo = nil |
| 112 | + StandSortManager.removeStandSort() | |
| 112 | 113 | if UserDefaults.standard.synchronize() { |
| 113 | 114 | print("用户信息删除成功") |
| 114 | 115 | } |
| 115 | 116 | } |
| 116 | 117 | } |
| 117 | 118 | |
| 119 | +class StandSortManager: NSObject { | |
| 120 | + | |
| 121 | + static let shared = StandSortManager() | |
| 122 | + | |
| 123 | + //根据用户信息设置key | |
| 124 | + static let sort_key: String = "mateCode_stand_sort_key_"+UserInfoManager.shared.userInfo.uid | |
| 125 | + fileprivate var _indexs : Array<String>? | |
| 126 | + private(set) var indexs: Array<String> { | |
| 127 | + set { | |
| 128 | + _indexs = newValue | |
| 129 | + } | |
| 130 | + | |
| 131 | + get { | |
| 132 | + return [] | |
| 133 | + } | |
| 134 | + } | |
| 135 | + | |
| 136 | + /// 存储排序信息 | |
| 137 | + /// - Parameter indexs: 排序的index | |
| 138 | + static private func cacheStandSort(indexs: NSArray) { | |
| 139 | + StandSortManager.shared._indexs = indexs as? Array<String> | |
| 140 | + UserDefaults.standard.set(indexs, forKey: sort_key) | |
| 141 | + if UserDefaults.standard.synchronize() { | |
| 142 | + print("排序信息存储成功") | |
| 143 | + } | |
| 144 | + } | |
| 145 | + | |
| 146 | + /// 获取排序信息 | |
| 147 | + /// - Returns: 返回信息 | |
| 148 | + static private func readStandSort() -> NSArray { | |
| 149 | + let indexs = UserDefaults.standard.object(forKey: sort_key) | |
| 150 | + if indexs == nil && (indexs as! NSArray).count == 0 { | |
| 151 | + print("获取排序信息失败") | |
| 152 | + return [] | |
| 153 | + } | |
| 154 | + StandSortManager.shared._indexs = indexs as? Array<String> | |
| 155 | + return indexs as! NSArray | |
| 156 | + } | |
| 157 | + | |
| 158 | + /// 移除排序信息 | |
| 159 | + static public func removeStandSort() { | |
| 160 | + UserDefaults.standard.removeObject(forKey: sort_key) | |
| 161 | + StandSortManager.shared._indexs = [] | |
| 162 | + if UserDefaults.standard.synchronize() { | |
| 163 | + print("排序信息删除成功") | |
| 164 | + } | |
| 165 | + } | |
| 166 | + | |
| 167 | + /// 对获取到的全部数据进行排序 | |
| 168 | + /// - Parameter stands: 获取到的数据 | |
| 169 | + /// - Returns: 返回 //<StandListModel> | |
| 170 | + static public func sortAllListData(stands: NSArray) -> NSArray { | |
| 171 | + let have_id_auths = StandSortManager.readStandSort() | |
| 172 | + if have_id_auths.count == 0 { | |
| 173 | + return stands | |
| 174 | + } | |
| 175 | + print("allList接口里面===>\(have_id_auths)") | |
| 176 | + let have_auths = NSMutableArray() | |
| 177 | + let no_auths = NSMutableArray() | |
| 178 | + for item in stands { | |
| 179 | + let listModel = item as! StandListModel | |
| 180 | + //这里需要及时去刷新认证状态 如果没有及时刷新这里回出现已经认证的状态不会进行排序 | |
| 181 | + listModel.auth && have_id_auths.contains(listModel.id) ? have_auths.add(listModel) : no_auths.add(listModel) | |
| 182 | + } | |
| 183 | + let tempArray = NSMutableArray() | |
| 184 | + for item in have_auths { | |
| 185 | + let standModel = item as! StandListModel | |
| 186 | + let old_index = have_auths.index(of: standModel) | |
| 187 | + let new_index = have_id_auths.index(of: standModel.id) | |
| 188 | + if old_index != new_index { | |
| 189 | + swap(&have_auths[new_index], &have_auths[old_index]) | |
| 190 | + } | |
| 191 | + } | |
| 192 | + tempArray.addObjects(from: have_auths as! [Any]) | |
| 193 | + tempArray.addObjects(from: no_auths as! [Any]) | |
| 194 | + return tempArray | |
| 195 | + } | |
| 196 | + | |
| 197 | + | |
| 198 | + /// 对已经获取到的认证身份进行排序 | |
| 199 | + /// - Parameter auths: 获取到的数据 | |
| 200 | + /// - Returns: 返回 //<MineAuthListModel> | |
| 201 | + static public func sortMyListData(auths: NSArray) -> NSArray { | |
| 202 | + let have_id_auths = StandSortManager.readStandSort() | |
| 203 | + if have_id_auths.count == 0 { | |
| 204 | + return auths | |
| 205 | + } | |
| 206 | + print("myList接口里面===>\(have_id_auths)") | |
| 207 | + let have_auths = NSMutableArray.init(array: auths) | |
| 208 | + for item in auths { | |
| 209 | + let standModel = item as! MineAuthListModel | |
| 210 | + let old_index = have_auths.index(of: standModel) | |
| 211 | + let new_index = have_id_auths.index(of: standModel.identityId) | |
| 212 | + if old_index != new_index { | |
| 213 | + swap(&have_auths[new_index], &have_auths[old_index]) | |
| 214 | + } | |
| 215 | + } | |
| 216 | + return have_auths | |
| 217 | + } | |
| 218 | + | |
| 219 | + /// 更新之后重新插入 | |
| 220 | + /// - Parameter indexs: 所以 | |
| 221 | + static public func insertSortData(indexs: NSArray) { | |
| 222 | + if (indexs.count > 0) { | |
| 223 | + StandSortManager.shared._indexs = [] | |
| 224 | + StandSortManager.removeStandSort() | |
| 225 | + StandSortManager.cacheStandSort(indexs: indexs) | |
| 226 | + } | |
| 227 | + } | |
| 228 | + | |
| 229 | +} | |
| 230 | + | |
| 118 | 231 | ... | ... |
metaCode/Classes/Config/Database/CNLiveSaveAuthDB.swift
| ... | ... | @@ -21,7 +21,7 @@ class CNLiveSaveAuthDB : NSObject{ |
| 21 | 21 | //重新init方法为私有,不让外部调用 |
| 22 | 22 | private override init() { |
| 23 | 23 | //数据库地址 |
| 24 | - let filePath = NSHomeDirectory() + "/Documents/DB/CNLiveAuthListTable.db" | |
| 24 | + let filePath = NSHomeDirectory() + "/Documents/DB"+UserInfoManager.shared.userInfo.uid+"/CNLiveAuthListTable.db" | |
| 25 | 25 | database = Database(at: filePath) |
| 26 | 26 | |
| 27 | 27 | super.init() |
| ... | ... | @@ -48,6 +48,10 @@ class CNLiveSaveAuthDB : NSObject{ |
| 48 | 48 | func insertObjects(objects: [MineAuthListModel]) { |
| 49 | 49 | if objects.count > 0 { |
| 50 | 50 | do{ |
| 51 | + for model in objects { | |
| 52 | + let m = model | |
| 53 | + print("插入时的数据结构===>\(m.identityId)") | |
| 54 | + } | |
| 51 | 55 | try database.insert(objects, intoTable: CNLiveAuthListTableName) |
| 52 | 56 | }catch{ |
| 53 | 57 | print("增加一条数据失败") |
| ... | ... | @@ -137,7 +141,8 @@ class CNLiveSaveAuthDB : NSObject{ |
| 137 | 141 | let allData: [MineAuthListModel] = try database.getObjects(fromTable: CNLiveAuthListTableName) |
| 138 | 142 | let tempArray = NSMutableArray.init() |
| 139 | 143 | for item in allData { |
| 140 | - tempArray.add("\(item.identityId)") | |
| 144 | + print("数据库查找之后数据==>\(item.identityId)") | |
| 145 | + tempArray.add(item.identityId) | |
| 141 | 146 | } |
| 142 | 147 | return tempArray as! Array<String> |
| 143 | 148 | }catch{ | ... | ... |
metaCode/Classes/Config/Database/CNLiveSaveStandDB.swift
| ... | ... | @@ -20,7 +20,7 @@ class CNLiveSaveStandDB: NSObject { |
| 20 | 20 | //重新init方法为私有,不让外部调用 |
| 21 | 21 | private override init() { |
| 22 | 22 | //数据库地址 |
| 23 | - let filePath = NSHomeDirectory() + "/Documents/DB/CNLiveStandListTableName.db" | |
| 23 | + let filePath = NSHomeDirectory() + "/Documents/DB"+UserInfoManager.shared.userInfo.uid+"/CNLiveStandListTableName.db" | |
| 24 | 24 | database = Database(at: filePath) |
| 25 | 25 | |
| 26 | 26 | super.init() | ... | ... |
metaCode/Classes/Main/MinePage/Controller/CNLiveMineAddInfoViewController.swift
| ... | ... | @@ -321,8 +321,13 @@ extension CNLiveMineAddInfoViewController { |
| 321 | 321 | to destinationIndexPath: IndexPath) { |
| 322 | 322 | objc_sync_enter(self) |
| 323 | 323 | swap(&dataArray[sourceIndexPath.row], &dataArray[destinationIndexPath.row]) |
| 324 | - CNLiveSaveAuthDB.shared.deleteAllObjects() | |
| 325 | - CNLiveSaveAuthDB.shared.insertObjects(objects: dataArray as! [MineAuthListModel]) | |
| 324 | + let tempArray = NSMutableArray() | |
| 325 | + for model in dataArray { | |
| 326 | + let m = model as! MineAuthListModel | |
| 327 | + tempArray.add(m.identityId) | |
| 328 | + } | |
| 329 | + print("索引存储的key=>\(tempArray)") | |
| 330 | + StandSortManager.insertSortData(indexs: tempArray) | |
| 326 | 331 | objc_sync_exit(self) |
| 327 | 332 | } |
| 328 | 333 | } | ... | ... |
metaCode/Classes/Main/MinePage/View/CNLiveMineBodyListView.swift
| ... | ... | @@ -51,6 +51,7 @@ class MineBodyOneListViewCell : UITableViewCell { |
| 51 | 51 | tipLabel.textColor = HexColor("#999999") |
| 52 | 52 | tipLabel.text = "您暂未进行身份认证" |
| 53 | 53 | tipLabel.font = Font_13 |
| 54 | + tipLabel.isHidden = true | |
| 54 | 55 | return tipLabel |
| 55 | 56 | }() |
| 56 | 57 | |
| ... | ... | @@ -67,6 +68,35 @@ class MineBodyOneListViewCell : UITableViewCell { |
| 67 | 68 | mainView.addSubview(lookBtn) |
| 68 | 69 | |
| 69 | 70 | setupConstraints() |
| 71 | + | |
| 72 | + | |
| 73 | + let tempArray = StandSortManager.sortMyListData(auths: CNLiveSaveAuthDB.shared.searchAllObject() as NSArray) as! [MineAuthListModel] | |
| 74 | + if tempArray.count == 0 { | |
| 75 | + tipLabel.isHidden = false | |
| 76 | + }else{ | |
| 77 | + for item in tempArray { | |
| 78 | + let authModel = item | |
| 79 | +// setupWithIconsView(authModel: authModel, index: index) | |
| 80 | + } | |
| 81 | + } | |
| 82 | + } | |
| 83 | + | |
| 84 | + private func setupWithIconsView(authModel: MineAuthListModel,index: NSInteger) { | |
| 85 | + | |
| 86 | + let iconView = UIImageView.init() | |
| 87 | + iconView.layer.cornerRadius = 10 | |
| 88 | + iconView.layer.masksToBounds = true | |
| 89 | + iconView.clipsToBounds = true | |
| 90 | + iconView.contentMode = .scaleToFill | |
| 91 | + iconView.backgroundColor = .white | |
| 92 | + iconView.kf.setImage(with: URL(string: authModel.icon ?? ""), placeholder: UIImage.imageWithColor(color: HexColor("#f9f9f9")!, size: CGSize(width: 20, height: 20))) | |
| 93 | + mainView.addSubview(iconView) | |
| 94 | + iconView.snp.makeConstraints { make in | |
| 95 | + make.centerY.equalToSuperview() | |
| 96 | + make.left.equalToSuperview().offset(10+(20 * index)-(index>1 ? 3:0)) | |
| 97 | + make.size.equalTo(20) | |
| 98 | + } | |
| 99 | + | |
| 70 | 100 | } |
| 71 | 101 | |
| 72 | 102 | required init?(coder: NSCoder) { | ... | ... |
metaCode/Classes/Main/MinePage/View/CNLiveMineSubBodyListView.swift
| ... | ... | @@ -157,7 +157,7 @@ class MineStandInfoTableViewCell: UITableViewCell { |
| 157 | 157 | public func setupWithAuthDataSource(icon: String, name: String,number: String) { |
| 158 | 158 | iconView.kf.setImage(with: URL(string: icon), placeholder: UIImage.imageWithColor(color: HexColor("#f9f9f9")!, size: CGSize(width: 50, height: 50))) |
| 159 | 159 | titleLabel.text = name |
| 160 | - numberLabel.text = number | |
| 160 | + numberLabel.text = "编号:\(number)" | |
| 161 | 161 | } |
| 162 | 162 | |
| 163 | 163 | public func setupWithMessageDataSource(icon: String, name: String,number: String, time: String) { | ... | ... |
metaCode/Classes/Main/MinePage/ViewModel/CNLiveMineBaseModel.swift
| ... | ... | @@ -181,12 +181,13 @@ final class StandListModel: BaseModel, TableCodable { |
| 181 | 181 | |
| 182 | 182 | enum CodingKeys: String, CodingTableKey { |
| 183 | 183 | typealias Root = StandListModel |
| 184 | - static let objectRelationalMapping = TableBinding(CodingKeys.self){ | |
| 184 | + static let objectRelationalMapping = TableBinding(CodingKeys.self) | |
| 185 | + { | |
| 185 | 186 | //设置表的主键key为id,用来区分数据唯一性, 如果id为int类型,可以按实现按顺序递增 |
| 186 | - BindColumnConstraint(id, isPrimary: true) | |
| 187 | + BindColumnConstraint(id, isPrimary: true, isAutoIncrement: true) | |
| 187 | 188 | } |
| 188 | - case id //不修改数据库字段名,和属性名保持一直 | |
| 189 | - case name //不修改数据库字段名,和属性名保持一直 | |
| 189 | + case id | |
| 190 | + case name | |
| 190 | 191 | case enable |
| 191 | 192 | case createTime |
| 192 | 193 | case createTimeString |
| ... | ... | @@ -263,10 +264,12 @@ final class MineAuthListModel: BaseModel , TableCodable { |
| 263 | 264 | var userStatus: Int = 0 |
| 264 | 265 | |
| 265 | 266 | enum CodingKeys: String, CodingTableKey { |
| 267 | + | |
| 266 | 268 | typealias Root = MineAuthListModel |
| 267 | - static let objectRelationalMapping = TableBinding(CodingKeys.self){ | |
| 269 | + static let objectRelationalMapping = TableBinding(CodingKeys.self) | |
| 270 | + { | |
| 268 | 271 | //设置表的主键key为id,用来区分数据唯一性, 如果id为int类型,可以按实现按顺序递增 |
| 269 | - BindColumnConstraint(identityId, isPrimary: true) | |
| 272 | + BindColumnConstraint(identityId, isPrimary: true, isAutoIncrement: true) | |
| 270 | 273 | } |
| 271 | 274 | case identityId //不修改数据库字段名,和属性名保持一直 |
| 272 | 275 | case name //不修改数据库字段名,和属性名保持一直 | ... | ... |
metaCode/Classes/Main/MinePage/ViewModel/CNLiveMineViewModel.swift
| ... | ... | @@ -265,46 +265,33 @@ class CNLiveMineViewModel: NSObject { |
| 265 | 265 | let errorCode = "\(resp["errorCode"] ?? "0")" |
| 266 | 266 | if (errorCode == "0") { |
| 267 | 267 | let listModel = dictionaryToModel(resp as! [String : Any], StandListBaseModel.self,"data") as! StandListBaseModel |
| 268 | - | |
| 269 | - if CNLiveSaveStandDB.shared.searchAllObject().count > 0 { | |
| 270 | - //第一页 | |
| 271 | - if page == 1 { | |
| 272 | - let have_id_auths = CNLiveSaveAuthDB.shared.searchAllObjectId() | |
| 273 | - let have_auths = NSMutableArray() | |
| 274 | - let no_auths = NSMutableArray() | |
| 275 | - for item in listModel.list { | |
| 276 | - //这里需要及时去刷新认证状态 如果没有及时刷新这里回出现已经认证的状态不会进行排序 | |
| 277 | - item.auth && have_id_auths.contains("\(item.id)") ? have_auths.add(item) : no_auths.add(item) | |
| 278 | - } | |
| 279 | - let tempArray = NSMutableArray() | |
| 280 | - for item in have_auths { | |
| 281 | - let standModel = item as! StandListModel | |
| 282 | - let old_index = have_auths.index(of: standModel) | |
| 283 | - let new_index = have_id_auths.firstIndex(of: "\(standModel.id)") | |
| 284 | - if old_index != new_index { | |
| 285 | - swap(&have_auths[new_index!], &have_auths[old_index]) | |
| 286 | - } | |
| 287 | - } | |
| 288 | - tempArray.addObjects(from: have_auths as! [Any]) | |
| 289 | - tempArray.addObjects(from: no_auths as! [Any]) | |
| 290 | - listModel.list = tempArray as! [StandListModel] | |
| 291 | - CNLiveSaveStandDB.shared.deleteAllObjects() | |
| 292 | - CNLiveSaveStandDB.shared.insertObjects(objects: listModel.list) | |
| 293 | - }else{ | |
| 294 | - | |
| 295 | - } | |
| 296 | - }else{ | |
| 268 | + if page == 1 && listModel.list.count > 0 { | |
| 297 | 269 | CNLiveSaveStandDB.shared.insertObjects(objects: listModel.list) |
| 298 | 270 | } |
| 299 | - | |
| 300 | - | |
| 271 | + listModel.list = StandSortManager.sortAllListData(stands: listModel.list as NSArray) as! [StandListModel] | |
| 301 | 272 | resultBlock(listModel,.success) |
| 302 | 273 | } else { |
| 303 | - showMessage(message: resp["errorMessage"] as! String) | |
| 304 | - resultBlock(NSNull(),.failed) | |
| 274 | + let tempArray = CNLiveSaveStandDB.shared.searchAllObject() | |
| 275 | + if tempArray.count > 0 { | |
| 276 | + let listModel = StandListBaseModel.init() | |
| 277 | + listModel.pageNum = page | |
| 278 | + listModel.total = page | |
| 279 | + listModel.list = StandSortManager.sortAllListData(stands: tempArray as NSArray) as! [StandListModel] | |
| 280 | + resultBlock(listModel,.failed) | |
| 281 | + }else{ | |
| 282 | + showMessage(message: resp["errorMessage"] as! String) | |
| 283 | + resultBlock(NSNull(),.failed) | |
| 284 | + } | |
| 305 | 285 | } |
| 306 | 286 | } else { |
| 307 | - if page > 1 { | |
| 287 | + let tempArray = CNLiveSaveStandDB.shared.searchAllObject() | |
| 288 | + if tempArray.count > 0 { | |
| 289 | + let listModel = StandListBaseModel.init() | |
| 290 | + listModel.pageNum = page | |
| 291 | + listModel.total = page | |
| 292 | + listModel.list = StandSortManager.sortAllListData(stands: tempArray as NSArray) as! [StandListModel] | |
| 293 | + resultBlock(listModel,.failed) | |
| 294 | + }else{ | |
| 308 | 295 | if CNLiveNetworking.isNetworking() { |
| 309 | 296 | showMessage(message: "获取失败") |
| 310 | 297 | resultBlock(NSNull(),.failed) |
| ... | ... | @@ -336,34 +323,33 @@ class CNLiveMineViewModel: NSObject { |
| 336 | 323 | let errorCode = "\(resp["errorCode"] ?? "0")" |
| 337 | 324 | if (errorCode == "0") { |
| 338 | 325 | let listModel = dictionaryToModel(resp as! [String : Any], MineAuthListBaseModel.self,"data") as! MineAuthListBaseModel |
| 339 | - if page == 1 { | |
| 340 | - let have_id_auths = CNLiveSaveAuthDB.shared.searchAllObjectId() | |
| 341 | - if have_id_auths.count > 0 { | |
| 342 | - let tempArray = NSMutableArray.init(array: listModel.list) | |
| 343 | - for item in tempArray { | |
| 344 | - if (have_id_auths.contains("\((item as! MineAuthListModel).identityId)")) { | |
| 345 | -// tempArray_have.add(item) | |
| 346 | - }else{ | |
| 347 | -// tempArray_no.add(item) | |
| 348 | - } | |
| 349 | - } | |
| 350 | -// listModel.list = tempArray as! [MineAuthListModel] | |
| 351 | - }else{ | |
| 352 | - //本地没值 不进行处理 | |
| 353 | - } | |
| 354 | - CNLiveSaveAuthDB.shared.deleteAllObjects() | |
| 326 | + if page == 1 && listModel.list.count > 0 { | |
| 355 | 327 | CNLiveSaveAuthDB.shared.insertObjects(objects: listModel.list) |
| 356 | - } else { | |
| 357 | - //多条数据 | |
| 358 | 328 | } |
| 359 | - | |
| 329 | + listModel.list = StandSortManager.sortMyListData(auths: listModel.list as NSArray) as! [MineAuthListModel] | |
| 360 | 330 | resultBlock(listModel,.success) |
| 361 | 331 | } else { |
| 362 | - showMessage(message: resp["errorMessage"] as! String) | |
| 363 | - resultBlock(NSNull(),.failed) | |
| 332 | + let tempArray = CNLiveSaveAuthDB.shared.searchAllObject() | |
| 333 | + if tempArray.count > 0 { | |
| 334 | + let listModel = MineAuthListBaseModel.init() | |
| 335 | + listModel.pageNum = page | |
| 336 | + listModel.total = page | |
| 337 | + listModel.list = StandSortManager.sortMyListData(auths: tempArray as NSArray) as! [MineAuthListModel] | |
| 338 | + resultBlock(listModel,.failed) | |
| 339 | + }else{ | |
| 340 | + showMessage(message: resp["errorMessage"] as! String) | |
| 341 | + resultBlock(NSNull(),.failed) | |
| 342 | + } | |
| 364 | 343 | } |
| 365 | 344 | } else { |
| 366 | - if page > 1 { | |
| 345 | + let tempArray = CNLiveSaveAuthDB.shared.searchAllObject() | |
| 346 | + if tempArray.count > 0 { | |
| 347 | + let listModel = MineAuthListBaseModel.init() | |
| 348 | + listModel.pageNum = page | |
| 349 | + listModel.total = page | |
| 350 | + listModel.list = StandSortManager.sortMyListData(auths: tempArray as NSArray) as! [MineAuthListModel] | |
| 351 | + resultBlock(listModel,.failed) | |
| 352 | + }else{ | |
| 367 | 353 | if CNLiveNetworking.isNetworking() { |
| 368 | 354 | showMessage(message: "获取失败") |
| 369 | 355 | resultBlock(NSNull(),.failed) | ... | ... |
metaCode/Classes/Other/AppDelegate.swift
| ... | ... | @@ -17,6 +17,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { |
| 17 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
| 18 | 18 | |
| 19 | 19 | // Override point for customization after application launch. |
| 20 | + | |
| 20 | 21 | |
| 21 | 22 | //设置APP环境 |
| 22 | 23 | CNLiveEnvironmentManager.shared.setupEnvironment(environment: .development) |
| ... | ... | @@ -59,6 +60,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { |
| 59 | 60 | CNLiveNetworking.setResponseSerializerType(.JSON) |
| 60 | 61 | CNLiveNetworking.setupBaseURL(APPLoginHost) |
| 61 | 62 | } |
| 63 | + | |
| 64 | + | |
| 62 | 65 | |
| 63 | 66 | |
| 64 | 67 | } | ... | ... |