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