diff --git a/features/patient/src/main/ets/components/PatientsGroup.ets b/features/patient/src/main/ets/components/PatientsGroup.ets index f940e9f..3ac6d15 100644 --- a/features/patient/src/main/ets/components/PatientsGroup.ets +++ b/features/patient/src/main/ets/components/PatientsGroup.ets @@ -14,16 +14,21 @@ export struct PatientsGroup { @State innerSort: string = '组内排序' @State groupSortList:sortModel[] = []; @State innerSortList:sortModel[] = []; - @State groupSortSelected: boolean = false//是否展开 - @State innerSortSelected: boolean = false//是否展开 - @State isGroupSelected: boolean = false//是否选中 - @State IsInnerSelected: boolean = false//是否选中 - @State isMaiLanHidden:boolean = false;//麦兰项目是否显示 + @State groupSortSelected: boolean = false + @State innerSortSelected: boolean = false + @State isGroupSelected: boolean = false + @State IsInnerSelected: boolean = false + @State isMaiLanHidden:boolean = false; @State group_sort:string = '0' @State list_sort:string = '0' - @State groupListArray: groupModel[] = [] + @State groupListArray: GroupModel[] = []; private params: ESObject = HMRouterMgr.getCurrentParam() + // 使用Map存储分组展开状态,键为分组UUID + private groupExpandStates: Map = new Map(); + + private groupDataSource: GroupDataSource = new GroupDataSource(); + dialog: CustomDialogController = new CustomDialogController({ builder: HdLoadingDialog({ message: '加载中...' }), customStyle: true, @@ -32,26 +37,37 @@ export struct PatientsGroup { aboutToAppear(): void { this.getGroupData() - this.getIsMaiLanData() this.groupSortList = [{"name":"按首字母","isSeleted":false} as sortModel,{"name":"分组人数","isSeleted":false} as sortModel] this.innerSortList = [{"name":"按首字母","isSeleted":false} as sortModel,{"name":"随访时间","isSeleted":false} as sortModel] } - getGroupData(){ + getGroupData() { this.dialog.open() hdHttp.post(BasicConstant.groupList, { expert_uuid: authStore.getUser().uuid, - group_sort:this.group_sort, - list_sort:this.list_sort + group_sort: this.group_sort, + list_sort: this.list_sort } as groupRequest).then(async (res: HdResponse) => { this.dialog.close(); - this.groupListArray = [] - logger.info('Response groupList'+res); - let json:groupRequestCall = JSON.parse(res+'') as groupRequestCall; - if(json.code == 1) { - this.groupListArray = json.data + logger.info('Response groupList' + res); + let json: groupRequestCall = JSON.parse(res + '') as groupRequestCall; + if (json.code == 1) { + // 创建新数据时应用保存的展开状态 + this.groupListArray = json.data.map(item => { + const group = new GroupModel(item); + + // 生成唯一分组标识 + const groupKey = this.generateGroupKey(group); + + // 如果之前保存过该分组的展开状态,则应用它 + if (this.groupExpandStates.has(groupKey)) { + group.isShow = this.groupExpandStates.get(groupKey) as boolean; + } + return group; + }); + this.groupDataSource.updateData(this.groupListArray); } else { - console.error('患者分组列表失败:'+json.message) + console.error('患者分组列表失败:' + json.message) promptAction.showToast({ message: json.message, duration: 1000 }) } }).catch((err: BusinessError) => { @@ -60,26 +76,23 @@ export struct PatientsGroup { }) } - getIsMaiLanData() { - const hashMap: HashMap = new HashMap(); - this.dialog.open() - hdHttp.httpReq(BasicConstant.isMaiLanExpert,hashMap).then(async (res: HdResponse) => { - this.dialog.close(); - logger.info('Response isMaiLanExpert'+res); - let json:Record = JSON.parse(res+'') as Record; - if(json.code == '200') { - let isMaiLanExpert:string = json.isMaiLanExpert; - if (isMaiLanExpert == '1') { - this.isMaiLanHidden = true; - } - } else { - console.error('麦兰:'+json.message) - promptAction.showToast({ message: json.message, duration: 1000 }) - } - }).catch((err: BusinessError) => { - this.dialog.close(); - console.info(`Response fails: ${err}`); - }) + // 生成唯一分组标识 + private generateGroupKey(group: GroupModel): string { + return `${group.uuid}_${group.name}`; + } + + // 处理分组展开/收起操作 + handleGroupToggle(group: GroupModel) { + // 生成唯一分组标识 + const groupKey = this.generateGroupKey(group); + + // 获取当前状态 + const currentState = this.groupExpandStates.get(groupKey) ?? false; + // 更新状态 + this.groupExpandStates.set(groupKey, !currentState); + + // 直接更新数据源中的状态 + this.groupDataSource.toggleGroupExpand(groupKey); } build() { @@ -90,16 +103,12 @@ export struct PatientsGroup { hasBorder: true, rightText: '新建', showRightText: true, - isLeftAction:this.params?true:false, - leftItemAction:()=>{ + isLeftAction: this.params ? true : false, + leftItemAction: () => { HMRouterMgr.pop() }, rightItemAction: () => { - HMRouterMgr.push({pageUrl:'BuildOrEditGroupPage',param:{"title":"新建分组"}}) - // router.pushUrl({ - // url: 'pages/PatientsPage/BuildOrEditGroupPage', - // params:{"title":"新建分组"} - // }) + HMRouterMgr.push({ pageUrl: 'BuildOrEditGroupPage', param: { "title": "新建分组" } }) } }) Stack() { @@ -107,7 +116,7 @@ export struct PatientsGroup { Row() { Text(this.groupSort) .fontSize(16).fontColor(this.isGroupSelected ? $r('app.color.main_color') : '#333333') - Image(this.isGroupSelected ?$r('app.media.triangle_green_theme'):$r('app.media.triangle_normal')).width(10).height(10) + Image(this.isGroupSelected ? $r('app.media.triangle_green_theme') : $r('app.media.triangle_normal')).width(10).height(10) } .width('50%') .height(48) @@ -123,7 +132,7 @@ export struct PatientsGroup { Row() { Text(this.innerSort) .fontSize(16).fontColor(this.IsInnerSelected ? $r('app.color.main_color') : '#333333') - Image(this.IsInnerSelected ?$r('app.media.triangle_green_theme'):$r('app.media.triangle_normal')).width(10).height(10) + Image(this.IsInnerSelected ? $r('app.media.triangle_green_theme') : $r('app.media.triangle_normal')).width(10).height(10) } .width('50%') .height(48) @@ -136,55 +145,48 @@ export struct PatientsGroup { }.width('100%').height(55).backgroundColor('#f4f4f4') List() { - ForEach(this.groupListArray, (sectionModel: groupModel,index:number) => { - ListItemGroup({ header: this.itemHeaderView(sectionModel,index) }) { - ForEach(sectionModel.isShow ? sectionModel.patientList : [], (rowModel: patientListModel) => { - ListItem() { - Stack() { - Row({ space: 15 }) { - Image(BasicConstant.urlImage + rowModel.photo) - .alt($r('app.media.userPhoto_default')) - .width(54) - .height(54) - .borderRadius(6) - .margin({ left: 15 }) - Text(rowModel.nickname ? rowModel.nickname : rowModel.realName) - .fontSize(16).fontColor('#666666') - if (Number(rowModel.type) === 0) { - Image($r('app.media.group_vip')) - .objectFit(ImageFit.Cover) - .width(10).height(10) + LazyForEach(this.groupDataSource, (item: FlatItem) => { + if (item.type === 'group') { + GroupHeader({ + group: item.group as GroupModel, + toggleExpand: () => { + animateTo({ duration: 300, curve: Curve.EaseOut }, () => { + // 调用统一处理方法 + this.handleGroupToggle(item.group as GroupModel); + }); + }, + onEdit: (group: GroupModel) => { + const pathInfo1: HMRouterPathInfo = { + pageUrl: 'BuildOrEditGroupPage', + param: { + "title": "编辑分组", + "group_uuid": group.uuid, + "group_name": group.name + } + }; + const callback: HMRouterPathCallback = { + onResult: (popInfo: PopInfo) => { + const result = popInfo.result as Record; + const isFreash = Boolean(result.nameString); + if (isFreash) { + this.getGroupData(); } - }.width('100%').height(80) - - Text('随访于' + rowModel.join_date?.substring(0, 10)) - .fontSize(14) - .fontColor('#999999') - .textAlign(TextAlign.End) - .margin({ right: 15 }) - .height(30) - Row() - .width('95%').height(0.5) - .backgroundColor('#999999') - }.width('100%').height(80).alignContent(Alignment.BottomEnd) - .onClick(()=>{ - HMRouterMgr.push({pageUrl:'PatientDetailsComp',param:{"patient_uuid":rowModel.uuid}}) - // router.pushUrl({ - // url:'pages/PatientsPage/PatientDetailsPage', - // params:{"patient_uuid":rowModel.uuid} - // }) - }) - }.width('100%') - }) + } + }; + HMRouterMgr.push(pathInfo1, callback); + } + }); + } else { + PatientItem({ patient: item.patient as patientListModel }); } - }) + }, (item: FlatItem) => this.generateKey(item)); } .width('100%') .height('calc(100% - 55vp - 56vp)') .backgroundColor('#f4f4f4') .scrollBar(BarState.Off) .sticky(StickyStyle.Header) - .margin({top:55}) + .margin({ top: 55 }) List() { ForEach(this.groupSortList, (item: sortModel) => { @@ -203,8 +205,8 @@ export struct PatientsGroup { }.width('100%').height(50).backgroundColor(Color.White) Blank() - .width('100%').height(1).backgroundColor($r('app.color.main_color')).margin({left:20}) - }.onClick(()=>{ + .width('100%').height(1).backgroundColor($r('app.color.main_color')).margin({ left: 20 }) + }.onClick(() => { this.isGroupSelected = true; this.innerSortSelected = false this.groupSortSelected = false @@ -222,8 +224,8 @@ export struct PatientsGroup { }) } }) - }.width('100%').height('calc(100% - 55vp)').backgroundColor('rgba(0,0,0,0.5)').margin({top:55}) - .visibility(this.groupSortSelected?Visibility.Visible:Visibility.Hidden) + }.width('100%').height('calc(100% - 55vp)').backgroundColor('rgba(0,0,0,0.5)').margin({ top: 55 }) + .visibility(this.groupSortSelected ? Visibility.Visible : Visibility.Hidden) List() { ForEach(this.innerSortList, (item: sortModel) => { @@ -244,7 +246,7 @@ export struct PatientsGroup { Blank() .width('95%').height(1).backgroundColor($r('app.color.main_color')) } - .onClick(()=>{ + .onClick(() => { this.IsInnerSelected = true this.innerSortSelected = false this.groupSortSelected = false @@ -262,73 +264,231 @@ export struct PatientsGroup { }) } }) - }.width('100%').height('calc(100% - 55vp)').backgroundColor('rgba(0,0,0,0.5)').margin({top:55}) - .visibility(this.innerSortSelected?Visibility.Visible:Visibility.Hidden) + }.width('100%').height('calc(100% - 55vp)').backgroundColor('rgba(0,0,0,0.5)').margin({ top: 55 }) + .visibility(this.innerSortSelected ? Visibility.Visible : Visibility.Hidden) Image($r('app.media.lifetime_right_icon')) .width(76).height(40) - .position({ x: '80%', y: '80%' }) // 定位到右下角 - .visibility(this.isMaiLanHidden?Visibility.Visible:Visibility.Hidden) + .position({ x: '80%', y: '80%' }) + .visibility(this.isMaiLanHidden ? Visibility.Visible : Visibility.Hidden) }.width('100%').height('calc(100% - 56vp)').backgroundColor('#f4f4f4').alignContent(Alignment.TopStart) } } - @Builder - itemHeaderView(model:groupModel,index:number) { + private generateKey(item: FlatItem): string { + if (item.type === 'group') { + return `group_${item.group?.uuid}_${item.group?.name}`; + } + return `patient_${item.patient?.uuid}_${item.group?.uuid}`; + } +} + +class GroupDataSource implements IDataSource { + private listeners: DataChangeListener[] = []; + private _groupList: GroupModel[] = []; + private flatData: FlatItem[] = []; + // 使用Map存储分组对象,键为唯一分组标识 + private groupMap: Map = new Map(); + + updateData(groupList: GroupModel[]) { + this._groupList = groupList; + + // 重置分组映射 + this.groupMap.clear(); + groupList.forEach(group => { + // 生成唯一分组标识 + const groupKey = `${group.uuid}_${group.name}`; + this.groupMap.set(groupKey, group); + }); + + this.flattenData(); + this.notifyDataReload(); + } + + private flattenData() { + this.flatData = []; + this._groupList.forEach((group) => { + const groupKey = `${group.uuid}_${group.name}`; + + this.flatData.push({ + type: 'group', + group: group as groupModel, + groupKey + }); + + if (group.isShow) { + group.patientList.forEach((patient) => { + this.flatData.push({ + type: 'patient', + patient, + groupKey + }); + }); + } + }); + } + + toggleGroupExpand(groupKey: string) { + const group = this.groupMap.get(groupKey); + if (group) { + group.isShow = !group.isShow; + this.flattenData(); + this.notifyDataReload(); + } + } + + totalCount(): number { return this.flatData.length; } + getData(index: number): FlatItem { return this.flatData[index]; } + + registerDataChangeListener(listener: DataChangeListener): void { + if (!this.listeners.includes(listener)) { + this.listeners.push(listener); + } + } + + unregisterDataChangeListener(listener: DataChangeListener): void { + const index = this.listeners.indexOf(listener); + if (index >= 0) this.listeners.splice(index, 1); + } + + notifyDataReload(): void { + this.listeners.forEach(listener => listener.onDataReloaded()); + } + + notifyDataChange(index: number): void { + this.listeners.forEach(listener => listener.onDataChange(index)); + } +} + +interface FlatItem { + type: 'group' | 'patient'; + group?: groupModel; + patient?: patientListModel; + groupKey: string; +} + +export class sortModel { + name?: string; + isSeleted: boolean = false; +} + +@Observed +class GroupModel { + uuid: string = ""; + name: string = ""; + patientNum: number = 0; + isShow: boolean = false; + patientList: patientListModel[] = []; + expert_uuid: string = ""; + type: number = 0; + isSelected: boolean = false; + + constructor(model?: groupModel) { + if (model) { + this.uuid = model.uuid || ""; + this.name = model.name || ""; + this.patientNum = model.patientNum || 0; + this.isShow = model.isShow || false; + this.patientList = model.patientList || []; + this.expert_uuid = model.expert_uuid || ""; + this.type = model.type || 0; + this.isSelected = model.isSelected || false; + } + } +} + +@Reusable +@Component +struct GroupHeader { + @ObjectLink group: GroupModel + private toggleExpand: () => void = () => {} + private onEdit: (group: GroupModel) => void = () => {} + + build() { Column() { Row() { - Image(model.isShow ? $r('app.media.group_turnDown') : $r('app.media.group_turnRight')) - .width(model.isShow ? 10 : 5).height(model.isShow ? 5 : 10).margin({ left: 15 }) - Text(model.name + ' | ' + model.patientNum) + Image(this.group.isShow ? $r('app.media.group_turnDown') : $r('app.media.group_turnRight')) + .width(this.group.isShow ? 10 : 5) + .height(this.group.isShow ? 5 : 10) + .margin({ left: 15 }) + + Text(this.group.name + ' | ' + this.group.patientNum) .fontSize(16) .fontColor('#333333') .margin({ left: 15 }) - .layoutWeight(1) - Text('编辑') - .width(60) - .height(60) - .fontSize(15) - .fontColor('#981308') - .margin({ right: 15 }) - .textAlign(TextAlign.End) - .visibility(model.name != '待分组患者'?Visibility.Visible:Visibility.Hidden) - .onClick(()=>{ - const pathInfo1: HMRouterPathInfo = { - pageUrl: 'BuildOrEditGroupPage', - param:{"title":"编辑分组","group_uuid":model.uuid,"group_name":model.name} - }; - const callback: HMRouterPathCallback = { - onResult: (popInfo: PopInfo) => { - const result = popInfo.result as Record - const isFreash = Boolean(result.nameString) - if (isFreash) { - this.getGroupData() - } - } - }; - HMRouterMgr.push(pathInfo1,callback) - // router.pushUrl({ - // url: 'pages/PatientsPage/BuildOrEditGroupPage', - // params:{"title":"编辑分组","group_uuid":model.uuid,"group_name":model.name} - // }) - }) + .layoutWeight(1); + + if (this.group.name !== '待分组患者') { + Text('编辑') + .width(60) + .height(60) + .fontSize(15) + .fontColor('#981308') + .margin({ right: 15 }) + .textAlign(TextAlign.End) + .onClick(() => this.onEdit(this.group)); + } } .width('100%') .height(60) .onClick(() => { - let newModel = new groupModel(model); - newModel.isShow = !model.isShow; - this.groupListArray[index] = newModel; - this.groupListArray = [...this.groupListArray]; - }) + this.toggleExpand(); + }); + Blank() - .width('100%').height(1).backgroundColor('#666666') - }.width('100%').height(61).backgroundColor(Color.White) + .width('100%') + .height(1) + .backgroundColor('#666666'); + } + .width('100%') + .height(61) + .backgroundColor(Color.White) } } -export class sortModel { - name?:string; - isSeleted:boolean = false; +@Component +struct PatientItem { + private patient: patientListModel = {} as patientListModel; + + build() { + Stack() { + Row({ space: 15 }) { + Image(BasicConstant.urlImage + this.patient.photo) + .alt($r('app.media.userPhoto_default')) + .width(54) + .height(54) + .borderRadius(6) + .margin({ left: 15 }); + Text(this.patient.nickname ? this.patient.nickname : this.patient.realName) + .fontSize(16) + .fontColor('#666666'); + if (Number(this.patient.type) === 0) { + Image($r('app.media.group_vip')) + .objectFit(ImageFit.Cover) + .width(10) + .height(10); + } + } + .width('100%') + .height(80); + + Text('随访于' + this.patient.join_date?.substring(0, 10)) + .fontSize(14) + .fontColor('#999999') + .textAlign(TextAlign.End) + .margin({ right: 15 }) + .height(30); + Row() + .width('95%') + .height(0.5) + .backgroundColor('#999999'); + } + .width('100%') + .height(80) + .alignContent(Alignment.BottomEnd) + .onClick(() => { + HMRouterMgr.push({ pageUrl: 'PatientDetailsComp', param: { "patient_uuid": this.patient.uuid } }); + }); + } } \ No newline at end of file