• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-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 { FilesData, TransList, MkDirItem } from '../../databases/model/FileData';
17import { on } from '../../base/utils/EventBus';
18import FileAccessExec from '../../base/utils/FileAccessExec';
19import { TREE_LAYER } from '../../base/constants/UiConstant';
20import { FOLDER_LEVEL } from '../../base/constants/Constant';
21import { FileUtil } from '../../base/utils/FileUtil';
22import { ArrayUtil } from '../../base/utils/ArrayUtil';
23import { FileBase } from '../../databases/model/base/FileBase';
24import Logger from '../../base/log/Logger';
25import fileAccess from '@ohos.file.fileAccess';
26import ObjectUtil from '../../base/utils/ObjectUtil';
27
28
29@Styles
30function pressedStyles() {
31  .backgroundColor($r('app.color.hicloud_hmos_bg'))
32}
33
34const TAG = 'TreeItem';
35
36@Component
37export struct TreeItem {
38  fileItem: FilesData = new FilesData();
39  loadPath: string = '';
40  isNeedExpand: boolean = false;
41  @State iconRotate: boolean = false;
42  @State subFolderList: FilesData[] = [];
43  @Link chooseItem: FilesData;
44  @Link selectUri: string;
45  @Link selectName: string;
46  @Link fileList: FilesData[];
47  @Link folderList: FilesData[];
48  @State isShowArrow: boolean = true;
49  @Prop layer: number;
50  @State isLoading: boolean = false;
51  @Link @Watch('clickExpandChange') isClickExpand: boolean;
52
53  private changeSelectItem(selectedItem: FilesData, autoShow: boolean) {
54    if (selectedItem) {
55      selectedItem.autoShow = autoShow;
56      this.chooseItem = selectedItem;
57      this.selectUri = this.chooseItem.uri;
58      this.selectName = this.chooseItem.fileName;
59    }
60  }
61
62  private async executeQuery(dirUri: string, defaultExpandPath: string, call: Function) {
63    this.isLoading = true;
64    if (!this.isNeedExpand || (this.isNeedExpand && !this.isClickExpand)) {
65      this.changeSelectItem(this.fileItem, false);
66    }
67
68    let queryRes = await this.getPickPathListFiles(dirUri, defaultExpandPath, this.fileItem.layer);
69    this.isLoading = false;
70    let subList: FilesData[] = this.fileBaseToFileData(queryRes);
71    let transList: TransList = this.transfer(subList);
72    this.fileList = transList.fileList
73    call(transList.folderList);
74  }
75
76  private async getPickPathListFiles(dirUri: string, expandPath: string, level: number): Promise<FileBase[]> {
77    let fileHelper = await FileUtil.getFileAccessHelperAsync(getContext());
78    let fileInfo: fileAccess.FileInfo = await FileUtil.getFileInfoByUri(dirUri, fileHelper);
79    if (ObjectUtil.isNullOrUndefined(fileInfo) || !FileUtil.isFolder(fileInfo.mode)) {
80      Logger.e(TAG, 'uri is not folder');
81      return [];
82    }
83    let queryRes = FileAccessExec.getPathPickSubFiles(fileInfo, expandPath, level);
84    if (ObjectUtil.isNull(queryRes)) {
85      Logger.e(TAG, 'files is null');
86      return [];
87    }
88    return queryRes;
89  }
90
91  transfer(list: FilesData[]): TransList {
92    let transList: TransList = {
93      folderList: [],
94      fileList: []
95    }
96    if (ArrayUtil.isEmpty(list)) {
97      return transList;
98    }
99    for (let i = 0; i < list.length; i++) {
100      let fileData = list[i];
101      if (fileData.isFolder) {
102        transList.folderList.push(fileData);
103      } else {
104        transList.fileList.push(fileData);
105      }
106    }
107    return transList;
108  }
109
110  fileBaseToFileData(list: FileBase[]): FilesData[] {
111    let fileArray: FilesData[] = [];
112    if (ArrayUtil.isEmpty(list)) {
113      return fileArray;
114    }
115    for (let i = 0; i < list.length; i++) {
116      let data = list[i];
117      let fileData = new FilesData();
118      fileData.uri = data.uri;
119      fileData.fileName = data.fileName;
120      fileData.isFolder = data.isFolder;
121      fileData.size = data.fileSize;
122      fileData.mtime = data.modifyTime;
123      fileData.path = data.relativePath;
124      fileData.currentDir = data.currentDir;
125      if (data.isFolder) {
126        if (!ArrayUtil.isEmpty(data.subList)) {
127          fileData.setSubList(this.fileBaseToFileData(data.subList));
128        }
129      }
130      fileArray.push(fileData);
131    }
132    return fileArray;
133  }
134
135  /**
136   * 是否需要展开目录,如果最近保存的目录不为空,需要展开到最近保存的目录
137   *
138   * @returns true:需要展开目录
139   */
140  needExpandPath(): boolean {
141    if (!this.canExpandPath() || this.isClickExpand) {
142      return false;
143    }
144    return FileUtil.hasSubFolder(this.loadPath, this.fileItem.currentDir);
145  }
146
147  clickExpandChange() {
148    this.isNeedExpand = false;
149    this.loadPath = '';
150  }
151
152  canExpandPath(): boolean {
153    return this.layer <= FOLDER_LEVEL.MAX_LEVEL;
154  }
155
156  loadSubFolder(subFolderList: FilesData[]) {
157    this.subFolderList = subFolderList;
158    this.folderList = this.subFolderList;
159    this.fileItem.setSubFolderList(subFolderList);
160    Logger.i(TAG, 'loadSubFolder:selectUri = ' + this.selectUri +
161      ' ; subFolderListSize = ' + this.subFolderList.length +
162      ' ; iconRotate = ' + this.iconRotate);
163  }
164
165  aboutToAppear() {
166    on('fileMkdir', async (e: MkDirItem) => {
167      if (this.selectUri === this.fileItem.uri) {
168        // 获取当前选中文件夹下的所有子文件
169        let queryArray = await this.getPickPathListFiles(this.fileItem.uri, '', this.fileItem.layer);
170        let subList: FilesData[] = this.fileBaseToFileData(queryArray)
171        let transList: TransList = this.transfer(subList);
172        this.fileList = transList.fileList;
173        // 获取当前选中文件夹下的所有子文件
174        this.subFolderList = transList.folderList;
175        this.expandSubFolderCall(transList.folderList);
176        // 查找刚刚新建的文件夹index
177        const index = this.subFolderList.findIndex(item => item.fileName === e.mkdirName);
178        if (index !== -1 && this.canExpandPath()) {
179          // 默认选中刚刚新建的文件夹
180          this.changeSelectItem(this.subFolderList[index], true);
181          this.iconRotate = true;
182          this.fileList = [];
183          this.folderList = [];
184        }
185      }
186    })
187
188    this.fileItem.setLayer(this.layer);
189    this.isNeedExpand = this.needExpandPath();
190    if (this.isNeedExpand) {
191      Logger.i(TAG, 'NeedExpand:loadPath = ' + this.loadPath +
192        ' ; path = ' + this.fileItem.currentDir);
193      this.clickExpand(false);
194    }
195  }
196
197  clickExpand(forceLoading: boolean) {
198    if (!this.isLoading) {
199      if (this.iconRotate) {
200        this.iconRotate = !this.iconRotate;
201        this.changeSelectItem(this.fileItem, false);
202        this.fileItem.subFileList = [];
203        this.folderList = this.fileItem.subFolderList;
204      } else {
205        if (this.canExpandPath()) {
206          if (this.fileItem.hasSubFolderList() && !forceLoading) {
207            this.changeSelectItem(this.fileItem, false);
208            this.fileList = this.fileItem.subFileList;
209            this.expandSubFolderCall(this.fileItem.getSubFolderList());
210          } else {
211            this.executeQuery(this.fileItem.uri, this.loadPath, this.expandSubFolderCall.bind(this));
212          }
213        }
214      }
215    }
216  }
217
218  private expandSubFolderCall(subFolderList: FilesData[]) {
219    this.iconRotate = !this.iconRotate;
220    this.loadSubFolder(subFolderList);
221    this.isShowArrow = this.subFolderList.length !== 0;
222  }
223
224  build() {
225    Column() {
226      Row() {
227        Image($r('app.media.hidisk_ic_list_empty_folder'))
228          .objectFit(ImageFit.Contain)
229          .renderMode(ImageRenderMode.Original)
230          .aspectRatio(1)
231          .width($r('app.float.common_size24'))
232          .alignSelf(ItemAlign.Center)
233          .margin({ right: $r('app.float.common_margin16') })
234        Text(this.fileItem.fileName)
235          .fontSize($r('app.float.common_font_size16'))
236          .layoutWeight(1)
237          .maxLines(1)
238          .textOverflow({ overflow: TextOverflow.Ellipsis })
239        if (this.isLoading) {
240          LoadingProgress()
241            .width($r('app.float.common_size24'))
242            .height($r('app.float.common_size24'))
243            .color($r('sys.color.ohos_id_color_text_secondary'))
244        } else {
245          Image($r('app.media.ic_arrow_right'))
246            .objectFit(ImageFit.Contain)
247            .autoResize(true)
248            .height($r('app.float.common_size12'))
249            .width($r('app.float.common_size12'))
250            .interpolation(ImageInterpolation.Medium)
251            .rotate({ z: 90, angle: this.iconRotate ? 90 : 0 })
252            .visibility(this.isShowArrow ? Visibility.Visible : Visibility.None)
253        }
254      }
255      .width('100%')
256      .padding({
257        top: $r('app.float.common_padding16'),
258        bottom: $r('app.float.common_padding16'),
259        right: $r('app.float.common_padding24'),
260        left: this.layer * TREE_LAYER + 'vp'
261      })
262      .backgroundColor(this.selectUri === this.fileItem.uri ? $r('app.color.move_dialog_background') : '')
263      .stateStyles({
264        pressed: pressedStyles
265      })
266      .onClick(() => {
267        this.isClickExpand = true;
268        this.clickExpand(true);
269      })
270
271      if (this.subFolderList.length && this.iconRotate) {
272        ForEach(this.subFolderList, (item: FilesData) => {
273          TreeItem({
274            fileItem: item,
275            loadPath: this.loadPath,
276            chooseItem: $chooseItem,
277            selectUri: $selectUri,
278            selectName: $selectName,
279            layer: this.layer + 1,
280            folderList: $folderList,
281            fileList: $fileList,
282            isClickExpand: $isClickExpand
283          })
284        })
285      }
286    }
287  }
288}