1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { BusinessError } from '@ohos.base'; 17import { staffItem } from './staff'; 18import Constants from '../constant'; 19import { AccountTipsConfig } from '../AccountTipsConfig'; 20import { HiLog } from '../HiLog'; 21import CommonUtil from '../CommonUtil'; 22import AccountManager from '../../manager/AccountManager'; 23import DomainAccountResponse from '../../bean/response/DomainAccountResponse'; 24import AppStorageConstant from '../constant/AppStorageConstant'; 25import { common } from '@kit.AbilityKit'; 26import DomainAccountInfo from '../../bean/data/DomainAccountInfo'; 27 28interface Staff { 29 authAccount: string; 30 textContent: string; 31} 32 33const TAG = 'AddStaff'; 34 35@Extend(Text) 36function inputMessageText() { 37 .fontSize($r('sys.float.ohos_id_text_size_body3')) 38 .lineHeight(Constants.PP_TEXT_LINE_HEIGHT2) 39 .fontColor($r('sys.color.ohos_id_color_handup')) 40 .fontWeight(FontWeight.Medium) 41 .margin({ top: Constants.ENCRYPTION_ADD_STAFF_BORDER_MARGIN_TOP }) 42 .textAlign(TextAlign.Start) 43} 44 45@Component 46struct AddStaff { 47 @State succ: number = 0; 48 @State fail: number = 0; 49 @Link isAccountCheckSuccess: boolean; 50 @State staffArrayLength: boolean = false; 51 @State @Watch('onErrorStyleChange') isInputInvalid: boolean = false; 52 @State isNetworkInvalid: boolean = false; 53 @State textContent: string = ''; 54 @Link @Watch('onDataChange') staffArray: Staff[]; 55 @State focusFlag: boolean = false; 56 @Prop isDisable: boolean = false; 57 @State isInitDataStatus: boolean = false; 58 @State errInput: string[] = []; 59 @State inputArray: string[] = []; 60 @State isInputInvalidFlag: boolean = false; 61 private controller: RichEditorController = new RichEditorController(); 62 private options: RichEditorOptions = { controller: this.controller }; 63 64 private shardingCount: number = 0; 65 66 @Builder 67 StaffItemBuilder(authAccount: string, textContent: string, index: number) { 68 Column() { 69 staffItem({ 70 authAccount: authAccount, 71 textContent: textContent, 72 isActive: true, 73 changeIndex: Number(index) 74 }); 75 } 76 .alignItems(HorizontalAlign.Start); 77 } 78 79 removeItem(i: number) { 80 this.staffArray.splice(i, 1) 81 this.staffArrayLength = false; 82 } 83 84 private async onSubmitMock(inputId: string, startOffset: number[], endOffset: number[]) { 85 if (!inputId) { 86 return; 87 } 88 if (this.staffArray.length >= Constants.ENCRYPTION_ADD_STAFF_LENGTH_MAX) { 89 this.staffArrayLength = true; 90 this.deleteBuildSpan(startOffset, endOffset); 91 return; 92 } 93 this.isAccountCheckSuccess = false; 94 let regex: RegExp = new RegExp('(\r|\n)*', 'g'); 95 let inputString = inputId.replace(regex, ''); 96 this.inputArray = inputString.split(';'); 97 this.errInput = []; 98 if (this.inputArray.length > Constants.RICH_EDITOR_FIRST) { 99 this.deleteBuildSpan(startOffset, endOffset); 100 } 101 102 await this.dealAccount(startOffset, endOffset); 103 104 this.isAccountCheckSuccess = true; 105 if (this.staffArray.length < Constants.ENCRYPTION_ADD_STAFF_LENGTH_MAX) { 106 this.controller.addTextSpan(this.errInput.join(';')); 107 if (this.errInput.length && this.isInputInvalidFlag) { 108 this.isInputInvalid = true; 109 } 110 } 111 } 112 113 private async dealAccount(startOffset: number[], endOffset: number[]) { 114 HiLog.info(TAG, 'dealAccount start'); 115 this.inputArray.filter(item =>{ 116 return !CommonUtil.isEmptyStr(item); 117 }); 118 this.shardingCount = this.inputArray.length / Constants.ENCRYPTION_ADD_STAFF_LENGTH_MAX; 119 let startLine: number = 0; 120 for (let i = 0; i < this.shardingCount; i++) { 121 if (this.staffArray.length >= Constants.ENCRYPTION_ADD_STAFF_LENGTH_MAX) { 122 this.errInput = []; 123 break; 124 } 125 let searchArray: string[] = this.inputArray.slice(startLine, (i + 1) * Constants.ENCRYPTION_ADD_STAFF_LENGTH_MAX); 126 let accountResponse: DomainAccountResponse | undefined = 127 await AccountManager.getDomainAccountByAccountNames(searchArray); 128 this.dealAccountResponse(searchArray, accountResponse, startOffset, endOffset); 129 130 startLine = (i + 1) * Constants.ENCRYPTION_ADD_STAFF_LENGTH_MAX; 131 } 132 HiLog.info(TAG, 'dealAccount end'); 133 } 134 135 private dealAccountResponse(searchArray: string[], accountResponse: DomainAccountResponse | undefined, 136 startOffset: number[], endOffset: number[]) { 137 let businessCode = this.getBusinessCode(accountResponse); 138 if (businessCode !== Constants.INTERFACE_SUCCESS) { 139 this.batchDealError(searchArray, businessCode, startOffset, endOffset); 140 return; 141 } 142 let dataArray: Array<DomainAccountInfo> | undefined = accountResponse?.getData(); 143 searchArray.forEach(accountName =>{ 144 let matchAccount = dataArray?.find(data => data.accountName === accountName); 145 if (matchAccount) { 146 this.addStaff(matchAccount); 147 if (this.inputArray.length === Constants.RICH_EDITOR_FIRST) { 148 this.deleteBuildSpan(startOffset, endOffset); 149 } 150 this.succ = CommonUtil.increaseByAppStorageKey(AppStorageConstant.ACCOUNT_VERIFY_SUCCESS_COUNT, 1); 151 this.addBuildSpan(accountName, matchAccount[this.textContent]); 152 } else { 153 // case: not found 154 this.showErrInput(accountName, Constants.ERR_JS_ACCOUNT_NOT_FOUND, startOffset, endOffset); 155 } 156 }) 157 } 158 159 private getBusinessCode(accountResponse: DomainAccountResponse | undefined): number { 160 if (accountResponse === undefined) { 161 // case: ipc error 162 return Constants.ERR_JS_NETWORK_INVALID; 163 } 164 if (accountResponse.getErrorCode() != Constants.INTERFACE_SUCCESS) { 165 // case: ipc success, but return error code 166 return accountResponse.getErrorCode(); 167 } 168 if (CommonUtil.isEmptyArray(accountResponse.getData())) { 169 // case: ipc success, but no data 170 return Constants.ERR_JS_ACCOUNT_NOT_FOUND; 171 } 172 return Constants.INTERFACE_SUCCESS; 173 } 174 175 private addStaff(matchAccount: DomainAccountInfo): void { 176 let staff: Staff = { 177 authAccount: matchAccount.accountName, 178 textContent: matchAccount[this.textContent] as string 179 }; 180 this.staffArray.push(staff); 181 } 182 183 private batchDealError(accountNameArray: string[], code: number, startOffset: number[], endOffset: number[]) { 184 accountNameArray.forEach(accountName =>{ 185 this.showErrInput(accountName, code, startOffset, endOffset); 186 }) 187 } 188 189 private deleteBuildSpan(startOffset: number[], endOffset: number[]) { 190 for (let i: number = startOffset.length - 1; i >= 0; i--) { 191 this.controller.deleteSpans({ start: startOffset[i], end: endOffset[i] }); 192 } 193 } 194 195 private addBuildSpan(staffName: string, textContent: string) { 196 let index: number = this.controller.getCaretOffset(); 197 let staffBuilder: CustomBuilder = () => { 198 this.StaffItemBuilder(staffName, textContent, index); 199 }; 200 this.controller.addBuilderSpan(staffBuilder); 201 } 202 203 private showErrInput(staffName: string, errorCode: number, startOffset: number[], endOffset: number[]) { 204 if (this.inputArray.length === Constants.RICH_EDITOR_FIRST) { 205 this.deleteBuildSpan(startOffset, endOffset); 206 } 207 this.errInput.push(staffName); 208 this.fail = CommonUtil.increaseByAppStorageKey(AppStorageConstant.ACCOUNT_VERIFY_FAIL_COUNT, 1); 209 if ([ 210 Constants.ERR_JS_INVALID_PARAMETER, 211 Constants.ERR_JS_ACCOUNT_NOT_FOUND 212 ].includes(errorCode)) { 213 this.isInputInvalidFlag = true; 214 } else { 215 this.isNetworkInvalid = true; 216 } 217 } 218 219 private onDataChange() { 220 !this.isInitDataStatus && this.staffArray && this.staffArray.forEach((item: Staff, index: number) => { 221 let staffItemBuilder: CustomBuilder = () => { 222 this.StaffItemBuilder(item.authAccount, item.textContent, index); 223 }; 224 this.controller.addBuilderSpan(staffItemBuilder); 225 }); 226 } 227 228 private onErrorStyleChange() { 229 this.controller.updateSpanStyle({ 230 textStyle: { 231 fontSize: $r('sys.float.ohos_id_text_size_body1'), 232 fontColor: this.isInputInvalid ? 233 $r('sys.color.ohos_id_color_handup') : $r('sys.color.ohos_id_color_text_primary') 234 } 235 }); 236 } 237 238 async aboutToAppear() { 239 AccountManager.connectAbility(getContext(this) as common.UIAbilityContext); 240 await AccountTipsConfig.getConfigTips(); 241 this.textContent = AccountTipsConfig.showContentKey; 242 if (this.staffArray.length) { 243 setTimeout(() => { 244 this.onDataChange(); 245 }, Constants.ENCRYPTION_SET_TIMEOUT_TIME); 246 } 247 } 248 249 build() { 250 Column() { 251 Flex({ 252 direction: FlexDirection.Row, 253 wrap: FlexWrap.Wrap, 254 }) { 255 RichEditor(this.options) 256 .onReady(() => { 257 this.controller.setTypingStyle({ 258 fontSize: $r('sys.float.ohos_id_text_size_body1') 259 }) 260 }) 261 .placeholder(!this.staffArray.length ? ($r('app.string.enter_a_complete_work_ID')) : '', 262 { 263 font: { size: $r('sys.float.ohos_id_text_size_body1') }, 264 fontColor: $r('sys.color.ohos_id_color_text_hint') 265 }) 266 .flexGrow(Constants.ENCRYPTION_ADD_STAFF_FLEX_GROW) 267 .backgroundColor($r('sys.color.ohos_id_color_dialog_bg')) 268 .borderRadius(Constants.PP_ROW_RADIUS) 269 .align(Alignment.Center) 270 .padding({ 271 top: Constants.PP_BUTTON_PAD, 272 bottom: Constants.PP_BUTTON_PAD, 273 left: Constants.PP_BUTTON_PAD, 274 right: Constants.PP_BUTTON_PAD 275 }) 276 .width(Constants.FOOTER_ROW_WIDTH) 277 .constraintSize({ 278 minHeight: Constants.RICH_EDITOR_MIN_HEIGHT 279 }) 280 .aboutToIMEInput((value: RichEditorInsertValue) => { 281 this.isInitDataStatus = true; 282 if (this.isInputInvalid || this.isNetworkInvalid) { 283 this.isInputInvalid = false; 284 this.isNetworkInvalid = false; 285 } 286 if (value.insertValue === Constants.ENTER_KEY_VALUE) { 287 let richEditorSpans: (RichEditorTextSpanResult | RichEditorImageSpanResult)[] = 288 this.controller.getSpans(); 289 let inputId: string = ''; 290 let startOffset: number[] = []; 291 let endOffset: number[] = []; 292 for (let index: number = 0; index < richEditorSpans.length; index++) { 293 let buildSpan: RichEditorTextSpanResult = richEditorSpans[index] as RichEditorTextSpanResult; 294 if (buildSpan.textStyle) { 295 inputId += buildSpan.value; 296 startOffset.push(buildSpan.spanPosition.spanRange[0]); 297 endOffset.push(buildSpan.spanPosition.spanRange[1]); 298 } 299 } 300 if (this.isAccountCheckSuccess) { 301 this.onSubmitMock(inputId, startOffset, endOffset); 302 }; 303 return false; 304 } 305 return true; 306 }) 307 .aboutToDelete((value: RichEditorDeleteValue) => { 308 if (!value.richEditorDeleteSpans.length) { 309 return false; 310 }; 311 this.isInitDataStatus = true; 312 if (this.isInputInvalid || this.isNetworkInvalid) { 313 this.isInputInvalid = false; 314 this.isNetworkInvalid = false; 315 } 316 317 let richEditorSpansAll: (RichEditorTextSpanResult | RichEditorImageSpanResult)[] = 318 this.controller.getSpans(); 319 320 let richEditorDeleteSpans: (RichEditorTextSpanResult | RichEditorImageSpanResult)[] = 321 value.richEditorDeleteSpans; 322 let len = richEditorDeleteSpans[richEditorDeleteSpans.length - 1].spanPosition.spanIndex; 323 let textNum: number = 0; 324 for (let i = 0; i <= len; i++) { 325 let buildSpan: RichEditorTextSpanResult = richEditorSpansAll[i] as RichEditorTextSpanResult; 326 if (buildSpan?.textStyle) { 327 textNum++; 328 } 329 } 330 for (let index: number = richEditorDeleteSpans.length - 1; index >= 0; index--) { 331 let buildSpan: RichEditorImageSpanResult = richEditorDeleteSpans[index] as RichEditorImageSpanResult; 332 if (buildSpan.imageStyle) { 333 let spanIndex: number = buildSpan.spanPosition.spanIndex; 334 spanIndex -= textNum; 335 this.removeItem(spanIndex); 336 } else { 337 textNum--; 338 } 339 } 340 return true; 341 }) 342 } 343 .onFocus(() => { 344 this.focusFlag = !this.focusFlag; 345 }) 346 .onBlur(() => { 347 this.focusFlag = !this.focusFlag; 348 }) 349 350 Divider() 351 .strokeWidth(this.focusFlag ? 352 px2vp(Constants.ENCRYPTION_ADD_STAFF_BORDER2) : px2vp(Constants.ENCRYPTION_ADD_STAFF_BORDER)) 353 .color((this.isInputInvalid || this.staffArrayLength || this.isNetworkInvalid) 354 ? $r('sys.color.ohos_id_color_handup') : 355 this.focusFlag ? $r('sys.color.ohos_id_color_primary') : $r('sys.color.ohos_id_color_list_separator')) 356 .opacity(this.focusFlag ? Constants.FOOTER_OPACITY_SEPC : Constants.FOOTER_OPACITY_ONE); 357 358 Flex({ direction: FlexDirection.Row }) { 359 if (this.isInputInvalid && !this.isNetworkInvalid) { 360 Text($r('app.string.incorrect_work_ID')) 361 .inputMessageText() 362 } 363 if (this.isNetworkInvalid) { 364 Text($r('app.string.network_invalid')) 365 .inputMessageText() 366 } 367 Blank() 368 if (this.staffArray.length >= 369 Constants.ENCRYPTION_ADD_STAFF_LENGTH_MAX * Constants.ENCRYPTION_ADD_STAFF_LENGTH) { 370 Text(`${this.staffArray.length}/${Constants.ENCRYPTION_ADD_STAFF_LENGTH_MAX}`) 371 .fontSize($r('sys.float.ohos_id_text_size_body3')) 372 .lineHeight(Constants.PP_TEXT_LINE_HEIGHT2) 373 .fontColor(this.staffArrayLength 374 ? $r('sys.color.ohos_id_color_handup') : $r('sys.color.ohos_id_color_text_secondary')) 375 .fontWeight(FontWeight.Medium) 376 .margin({ top: Constants.ENCRYPTION_ADD_STAFF_BORDER_MARGIN_TOP }) 377 .textAlign(TextAlign.End) 378 } 379 } 380 } 381 .opacity(this.isDisable ? Constants.DU_LINE_WIDTH : Constants.FOOTER_OPACITY_ONE) 382 .enabled(this.isDisable ? false : true) 383 } 384} 385 386export { AddStaff }; 387