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