@10yun/cv-mobile-ui 0.5.20 → 0.5.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/package.json +1 -1
  2. package/ui-cv/components/cv-grid-item/cv-grid-item.vue +1 -1
  3. package/uni-ui/lib/uni-badge/uni-badge.vue +150 -1
  4. package/uni-ui/lib/uni-breadcrumb/uni-breadcrumb.vue +37 -1
  5. package/uni-ui/lib/uni-breadcrumb-item/uni-breadcrumb-item.vue +83 -1
  6. package/uni-ui/lib/uni-calendar/uni-calendar-item.vue +122 -1
  7. package/uni-ui/lib/uni-calendar/uni-calendar.vue +366 -1
  8. package/uni-ui/lib/uni-card/uni-card.vue +124 -1
  9. package/uni-ui/lib/uni-col/uni-col.vue +1 -1
  10. package/uni-ui/lib/uni-collapse/uni-collapse.vue +135 -1
  11. package/uni-ui/lib/uni-collapse-item/uni-collapse-item.vue +266 -1
  12. package/uni-ui/lib/uni-combox/uni-combox.vue +1 -1
  13. package/uni-ui/lib/uni-countdown/uni-countdown.vue +239 -1
  14. package/uni-ui/lib/uni-data-checkbox/uni-data-checkbox.vue +487 -1
  15. package/uni-ui/lib/uni-data-picker/uni-data-picker.vue +530 -1
  16. package/uni-ui/lib/uni-data-pickerview/uni-data-picker.js +157 -150
  17. package/uni-ui/lib/uni-data-pickerview/uni-data-pickerview.vue +166 -1
  18. package/uni-ui/lib/uni-data-select/uni-data-select.vue +289 -1
  19. package/uni-ui/lib/uni-datetime-picker/calendar-item.vue +70 -1
  20. package/uni-ui/lib/uni-datetime-picker/calendar.vue +629 -1
  21. package/uni-ui/lib/uni-datetime-picker/time-picker.vue +741 -1
  22. package/uni-ui/lib/uni-datetime-picker/uni-datetime-picker.vue +847 -1
  23. package/uni-ui/lib/uni-drawer/uni-drawer.vue +115 -1
  24. package/uni-ui/lib/uni-easyinput/uni-easyinput.vue +515 -1
  25. package/uni-ui/lib/uni-fab/uni-fab.vue +257 -1
  26. package/uni-ui/lib/uni-fav/uni-fav.vue +123 -1
  27. package/uni-ui/lib/uni-file-picker/uni-file-picker.vue +642 -1
  28. package/uni-ui/lib/uni-file-picker/upload-file.vue +177 -1
  29. package/uni-ui/lib/uni-file-picker/upload-image.vue +176 -1
  30. package/uni-ui/lib/uni-forms/uni-forms.vue +375 -1
  31. package/uni-ui/lib/uni-forms-item/uni-forms-item.vue +429 -1
  32. package/uni-ui/lib/uni-goods-nav/uni-goods-nav.vue +129 -1
  33. package/uni-ui/lib/uni-grid/uni-grid.vue +115 -1
  34. package/uni-ui/lib/uni-grid-item/uni-grid-item.vue +78 -1
  35. package/uni-ui/lib/uni-group/uni-group.vue +85 -1
  36. package/uni-ui/lib/uni-icons/uni-icons.vue +85 -1
  37. package/uni-ui/lib/uni-indexed-list/uni-indexed-list-item.vue +68 -1
  38. package/uni-ui/lib/uni-indexed-list/uni-indexed-list.vue +294 -1
  39. package/uni-ui/lib/uni-list/uni-list.vue +81 -1
  40. package/uni-ui/lib/uni-list-ad/uni-list-ad.vue +77 -1
  41. package/uni-ui/lib/uni-list-chat/uni-list-chat.vue +294 -1
  42. package/uni-ui/lib/uni-list-item/uni-list-item.vue +346 -1
  43. package/uni-ui/lib/uni-load-more/uni-load-more.vue +172 -1
  44. package/uni-ui/lib/uni-nav-bar/uni-nav-bar.vue +205 -1
  45. package/uni-ui/lib/uni-nav-bar/uni-status-bar.vue +18 -1
  46. package/uni-ui/lib/uni-notice-bar/uni-notice-bar.vue +331 -1
  47. package/uni-ui/lib/uni-number-box/uni-number-box.vue +166 -1
  48. package/uni-ui/lib/uni-pagination/uni-pagination.vue +323 -1
  49. package/uni-ui/lib/uni-popup/uni-popup.vue +1 -1
  50. package/uni-ui/lib/uni-popup-dialog/uni-popup-dialog.vue +173 -1
  51. package/uni-ui/lib/uni-popup-message/uni-popup-message.vue +74 -1
  52. package/uni-ui/lib/uni-popup-share/uni-popup-share.vue +106 -1
  53. package/uni-ui/lib/uni-rate/uni-rate.vue +322 -1
  54. package/uni-ui/lib/uni-row/uni-row.vue +1 -1
  55. package/uni-ui/lib/uni-search-bar/uni-search-bar.vue +236 -1
  56. package/uni-ui/lib/uni-section/uni-section.vue +109 -1
  57. package/uni-ui/lib/uni-segmented-control/uni-segmented-control.vue +103 -1
  58. package/uni-ui/lib/uni-status-bar/uni-status-bar.vue +1 -1
  59. package/uni-ui/lib/uni-steps/uni-steps.vue +120 -1
  60. package/uni-ui/lib/uni-swipe-action-item/uni-swipe-action-item.vue +226 -3
  61. package/uni-ui/lib/uni-swiper-dot/uni-swiper-dot.vue +167 -1
  62. package/uni-ui/lib/uni-table/uni-table.vue +297 -1
  63. package/uni-ui/lib/uni-tag/uni-tag.vue +100 -1
  64. package/uni-ui/lib/uni-td/uni-td.vue +78 -1
  65. package/uni-ui/lib/uni-th/filter-dropdown.vue +1 -1
  66. package/uni-ui/lib/uni-th/uni-th.vue +224 -1
  67. package/uni-ui/lib/uni-thead/uni-thead.vue +77 -1
  68. package/uni-ui/lib/uni-tr/table-checkbox.vue +79 -1
  69. package/uni-ui/lib/uni-tr/uni-tr.vue +135 -1
@@ -1 +1,642 @@
1
- <template>
2
1
  <view class="uni-file-picker">
3
2
  <view v-if="title" class="uni-file-picker__header">
4
3
  <text class="file-title">{{ title }}</text>
5
4
  <text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
6
5
  </view>
7
6
  <upload-image
8
7
  v-if="fileMediatype === 'image' && showType === 'grid'"
9
8
  :readonly="readonly"
10
9
  :image-styles="imageStyles"
11
10
  :files-list="filesList"
12
11
  :limit="limitLength"
13
12
  :disablePreview="disablePreview"
14
13
  :delIcon="delIcon"
15
14
  @uploadFiles="uploadFiles"
16
15
  @choose="choose"
17
16
  @delFile="delFile"
18
17
  >
19
18
  <slot>
20
19
  <view class="is-add">
21
20
  <view class="icon-add"></view>
22
21
  <view class="icon-add rotate"></view>
23
22
  </view>
24
23
  </slot>
25
24
  </upload-image>
26
25
  <upload-file
27
26
  v-if="fileMediatype !== 'image' || showType !== 'grid'"
28
27
  :readonly="readonly"
29
28
  :list-styles="listStyles"
30
29
  :files-list="filesList"
31
30
  :showType="showType"
32
31
  :delIcon="delIcon"
33
32
  @uploadFiles="uploadFiles"
34
33
  @choose="choose"
35
34
  @delFile="delFile"
36
35
  >
37
36
  <slot><button type="primary" size="mini">选择文件</button></slot>
38
37
  </upload-file>
39
38
  </view>
40
39
  * FilePicker 文件选择上传
41
40
  * @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
42
41
  * @tutorial https://ext.dcloud.net.cn/plugin?id=4079
43
42
  * @property {Object|Array} value 组件数据,通常用来回显 ,类型由return-type属性决定
44
43
  * @property {Boolean} disabled = [true|false] 组件禁用
45
44
  * @value true 禁用
46
45
  * @value false 取消禁用
47
46
  * @property {Boolean} readonly = [true|false] 组件只读,不可选择,不显示进度,不显示删除按钮
48
47
  * @value true 只读
49
48
  * @value false 取消只读
50
49
  * @property {String} return-type = [array|object] 限制 value 格式,当为 object 时 ,组件只能单选,且会覆盖
51
50
  * @value array 规定 value 属性的类型为数组
52
51
  * @value object 规定 value 属性的类型为对象
53
52
  * @property {Boolean} disable-preview = [true|false] 禁用图片预览,仅 mode:grid 时生效
54
53
  * @value true 禁用图片预览
55
54
  * @value false 取消禁用图片预览
56
55
  * @property {Boolean} del-icon = [true|false] 是否显示删除按钮
57
56
  * @value true 显示删除按钮
58
57
  * @value false 不显示删除按钮
59
58
  * @property {Boolean} auto-upload = [true|false] 是否自动上传,值为true则只触发@select,可自行上传
60
59
  * @value true 自动上传
61
60
  * @value false 取消自动上传
62
61
  * @property {Number|String} limit 最大选择个数 ,h5 会自动忽略多选的部分
63
62
  * @property {String} title 组件标题,右侧显示上传计数
64
63
  * @property {String} mode = [list|grid] 选择文件后的文件列表样式
65
64
  * @value list 列表显示
66
65
  * @value grid 宫格显示
67
66
  * @property {String} file-mediatype = [image|video|all] 选择文件类型
68
67
  * @value image 只选择图片
69
68
  * @value video 只选择视频
70
69
  * @value all 选择所有文件
71
70
  * @property {Array} file-extname 选择文件后缀,根据 file-mediatype 属性而不同
72
71
  * @property {Object} list-style mode:list 时的样式
73
72
  * @property {Object} image-styles 选择文件后缀,根据 file-mediatype 属性而不同
74
73
  * @event {Function} select 选择文件后触发
75
74
  * @event {Function} progress 文件上传时触发
76
75
  * @event {Function} success 上传成功触发
77
76
  * @event {Function} fail 上传失败触发
78
77
  * @event {Function} delete 文件从列表移除时触发
79
78
  */
80
79
  name: 'uniFilePicker',
81
80
  components: {
82
81
  uploadImage,
83
82
  uploadFile
84
83
  },
85
84
  options: {
86
85
  virtualHost: true
87
86
  },
88
87
  emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'],
89
88
  props: {
90
89
  // #ifdef VUE3
91
90
  modelValue: {
92
91
  type: [Array, Object],
93
92
  default() {
94
93
  return [];
95
94
  }
96
95
  },
97
96
  // #endif
98
97
  // #ifndef VUE3
99
98
  value: {
100
99
  type: [Array, Object],
101
100
  default() {
102
101
  return [];
103
102
  }
104
103
  },
105
104
  // #endif
106
105
  disabled: {
107
106
  type: Boolean,
108
107
  default: false
109
108
  },
110
109
  disablePreview: {
111
110
  type: Boolean,
112
111
  default: false
113
112
  },
114
113
  delIcon: {
115
114
  type: Boolean,
116
115
  default: true
117
116
  },
118
117
  // 自动上传
119
118
  autoUpload: {
120
119
  type: Boolean,
121
120
  default: true
122
121
  },
123
122
  // 最大选择个数 ,h5只能限制单选或是多选
124
123
  limit: {
125
124
  type: [Number, String],
126
125
  default: 9
127
126
  },
128
127
  // 列表样式 grid | list | list-card
129
128
  mode: {
130
129
  type: String,
131
130
  default: 'grid'
132
131
  },
133
132
  // 选择文件类型 image/video/all
134
133
  fileMediatype: {
135
134
  type: String,
136
135
  default: 'image'
137
136
  },
138
137
  // 文件类型筛选
139
138
  fileExtname: {
140
139
  type: [Array, String],
141
140
  default() {
142
141
  return [];
143
142
  }
144
143
  },
145
144
  title: {
146
145
  type: String,
147
146
  default: ''
148
147
  },
149
148
  listStyles: {
150
149
  type: Object,
151
150
  default() {
152
151
  return {
153
152
  // 是否显示边框
154
153
  border: true,
155
154
  // 是否显示分隔线
156
155
  dividline: true,
157
156
  // 线条样式
158
157
  borderStyle: {}
159
158
  };
160
159
  }
161
160
  },
162
161
  imageStyles: {
163
162
  type: Object,
164
163
  default() {
165
164
  return {
166
165
  width: 'auto',
167
166
  height: 'auto'
168
167
  };
169
168
  }
170
169
  },
171
170
  readonly: {
172
171
  type: Boolean,
173
172
  default: false
174
173
  },
175
174
  returnType: {
176
175
  type: String,
177
176
  default: 'array'
178
177
  },
179
178
  sizeType: {
180
179
  type: Array,
181
180
  default() {
182
181
  return ['original', 'compressed'];
183
182
  }
184
183
  },
185
184
  sourceType: {
186
185
  type: Array,
187
186
  default() {
188
187
  return ['album', 'camera'];
189
188
  }
190
189
  }
191
190
  },
192
191
  data() {
193
192
  return {
194
193
  files: [],
195
194
  localValue: []
196
195
  };
197
196
  },
198
197
  watch: {
199
198
  // #ifndef VUE3
200
199
  value: {
201
200
  handler(newVal, oldVal) {
202
201
  this.setValue(newVal, oldVal);
203
202
  },
204
203
  immediate: true
205
204
  },
206
205
  // #endif
207
206
  // #ifdef VUE3
208
207
  modelValue: {
209
208
  handler(newVal, oldVal) {
210
209
  this.setValue(newVal, oldVal);
211
210
  },
212
211
  immediate: true
213
212
  }
214
213
  // #endif
215
214
  },
216
215
  computed: {
217
216
  filesList() {
218
217
  let files = [];
219
218
  this.files.forEach((v) => {
220
219
  files.push(v);
221
220
  });
222
221
  return files;
223
222
  },
224
223
  showType() {
225
224
  if (this.fileMediatype === 'image') {
226
225
  return this.mode;
227
226
  }
228
227
  return 'list';
229
228
  },
230
229
  limitLength() {
231
230
  if (this.returnType === 'object') {
232
231
  return 1;
233
232
  }
234
233
  if (!this.limit) {
235
234
  return 1;
236
235
  }
237
236
  if (this.limit >= 9) {
238
237
  return 9;
239
238
  }
240
239
  return this.limit;
241
240
  }
242
241
  },
243
242
  created() {
244
243
  // TODO 兼容不开通服务空间的情况
245
244
  if (!(uniCloud.config && uniCloud.config.provider)) {
246
245
  this.noSpace = true;
247
246
  uniCloud.chooseAndUploadFile = chooseAndUploadFile;
248
247
  }
249
248
  this.form = this.getForm('uniForms');
250
249
  this.formItem = this.getForm('uniFormsItem');
251
250
  if (this.form && this.formItem) {
252
251
  if (this.formItem.name) {
253
252
  this.rename = this.formItem.name;
254
253
  this.form.inputChildrens.push(this);
255
254
  }
256
255
  }
257
256
  },
258
257
  methods: {
259
258
  /**
260
259
  * 公开用户使用,清空文件
261
260
  * @param {Object} index
262
261
  */
263
262
  clearFiles(index) {
264
263
  if (index !== 0 && !index) {
265
264
  this.files = [];
266
265
  this.$nextTick(() => {
267
266
  this.setEmit();
268
267
  });
269
268
  } else {
270
269
  this.files.splice(index, 1);
271
270
  }
272
271
  this.$nextTick(() => {
273
272
  this.setEmit();
274
273
  });
275
274
  },
276
275
  /**
277
276
  * 公开用户使用,继续上传
278
277
  */
279
278
  upload() {
280
279
  let files = [];
281
280
  this.files.forEach((v, index) => {
282
281
  if (v.status === 'ready' || v.status === 'error') {
283
282
  files.push(Object.assign({}, v));
284
283
  }
285
284
  });
286
285
  return this.uploadFiles(files);
287
286
  },
288
287
  async setValue(newVal, oldVal) {
289
288
  const newData = async (v) => {
290
289
  const reg = /cloud:\/\/([\w.]+\/?)\S*/;
291
290
  let url = '';
292
291
  if (v.fileID) {
293
292
  url = v.fileID;
294
293
  } else {
295
294
  url = v.url;
296
295
  }
297
296
  if (reg.test(url)) {
298
297
  v.fileID = url;
299
298
  v.url = await this.getTempFileURL(url);
300
299
  }
301
300
  if (v.url) v.path = v.url;
302
301
  return v;
303
302
  };
304
303
  if (this.returnType === 'object') {
305
304
  if (newVal) {
306
305
  await newData(newVal);
307
306
  } else {
308
307
  newVal = {};
309
308
  }
310
309
  } else {
311
310
  if (!newVal) newVal = [];
312
311
  for (let i = 0; i < newVal.length; i++) {
313
312
  let v = newVal[i];
314
313
  await newData(v);
315
314
  }
316
315
  }
317
316
  this.localValue = newVal;
318
317
  if (this.form && this.formItem && !this.is_reset) {
319
318
  this.is_reset = false;
320
319
  this.formItem.setValue(this.localValue);
321
320
  }
322
321
  let filesData = Object.keys(newVal).length > 0 ? newVal : [];
323
322
  this.files = [].concat(filesData);
324
323
  },
325
324
  /**
326
325
  * 选择文件
327
326
  */
328
327
  choose() {
329
328
  if (this.disabled) return;
330
329
  if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType === 'array') {
331
330
  uni.showToast({
332
331
  title: `您最多选择 ${this.limitLength} 个文件`,
333
332
  icon: 'none'
334
333
  });
335
334
  return;
336
335
  }
337
336
  this.chooseFiles();
338
337
  },
339
338
  /**
340
339
  * 选择文件并上传
341
340
  */
342
341
  chooseFiles() {
343
342
  const _extname = get_extname(this.fileExtname);
344
343
  // 获取后缀
345
344
  uniCloud
346
345
  .chooseAndUploadFile({
347
346
  type: this.fileMediatype,
348
347
  compressed: false,
349
348
  sizeType: this.sizeType,
350
349
  sourceType: this.sourceType,
351
350
  // TODO 如果为空,video 有问题
352
351
  extension: _extname.length > 0 ? _extname : undefined,
353
352
  count: this.limitLength - this.files.length, //默认9
354
353
  onChooseFile: this.chooseFileCallback,
355
354
  onUploadProgress: (progressEvent) => {
356
355
  this.setProgress(progressEvent, progressEvent.index);
357
356
  }
358
357
  })
359
358
  .then((result) => {
360
359
  this.setSuccessAndError(result.tempFiles);
361
360
  })
362
361
  .catch((err) => {
363
362
  console.log('选择失败', err);
364
363
  });
365
364
  },
366
365
  /**
367
366
  * 选择文件回调
368
367
  * @param {Object} res
369
368
  */
370
369
  async chooseFileCallback(res) {
371
370
  const _extname = get_extname(this.fileExtname);
372
371
  const is_one = (Number(this.limitLength) === 1 && this.disablePreview && !this.disabled) || this.returnType === 'object';
373
372
  // 如果这有一个文件 ,需要清空本地缓存数据
374
373
  if (is_one) {
375
374
  this.files = [];
376
375
  }
377
376
  let { filePaths, files } = get_files_and_is_max(res, _extname);
378
377
  if (!(_extname && _extname.length > 0)) {
379
378
  filePaths = res.tempFilePaths;
380
379
  files = res.tempFiles;
381
380
  }
382
381
  let currentData = [];
383
382
  for (let i = 0; i < files.length; i++) {
384
383
  if (this.limitLength - this.files.length <= 0) break;
385
384
  files[i].uuid = Date.now();
386
385
  let filedata = await get_file_data(files[i], this.fileMediatype);
387
386
  filedata.progress = 0;
388
387
  filedata.status = 'ready';
389
388
  this.files.push(filedata);
390
389
  currentData.push({
391
390
  ...filedata,
392
391
  file: files[i]
393
392
  });
394
393
  }
395
394
  this.$emit('select', {
396
395
  tempFiles: currentData,
397
396
  tempFilePaths: filePaths
398
397
  });
399
398
  res.tempFiles = files;
400
399
  // 停止自动上传
401
400
  if (!this.autoUpload || this.noSpace) {
402
401
  res.tempFiles = [];
403
402
  }
404
403
  },
405
404
  /**
406
405
  * 批传
407
406
  * @param {Object} e
408
407
  */
409
408
  uploadFiles(files) {
410
409
  files = [].concat(files);
411
410
  return uploadCloudFiles
412
411
  .call(this, files, 5, (res) => {
413
412
  this.setProgress(res, res.index, true);
414
413
  })
415
414
  .then((result) => {
416
415
  this.setSuccessAndError(result);
417
416
  return result;
418
417
  })
419
418
  .catch((err) => {
420
419
  console.log(err);
421
420
  });
422
421
  },
423
422
  /**
424
423
  * 成功或失败
425
424
  */
426
425
  async setSuccessAndError(res, fn) {
427
426
  let successData = [];
428
427
  let errorData = [];
429
428
  let tempFilePath = [];
430
429
  let errorTempFilePath = [];
431
430
  for (let i = 0; i < res.length; i++) {
432
431
  const item = res[i];
433
432
  const index = item.uuid ? this.files.findIndex((p) => p.uuid === item.uuid) : item.index;
434
433
  if (index === -1 || !this.files) break;
435
434
  if (item.errMsg === 'request:fail') {
436
435
  this.files[index].url = item.path;
437
436
  this.files[index].status = 'error';
438
437
  this.files[index].errMsg = item.errMsg;
439
438
  // this.files[index].progress = -1
440
439
  errorData.push(this.files[index]);
441
440
  errorTempFilePath.push(this.files[index].url);
442
441
  } else {
443
442
  this.files[index].errMsg = '';
444
443
  this.files[index].fileID = item.url;
445
444
  const reg = /cloud:\/\/([\w.]+\/?)\S*/;
446
445
  if (reg.test(item.url)) {
447
446
  this.files[index].url = await this.getTempFileURL(item.url);
448
447
  } else {
449
448
  this.files[index].url = item.url;
450
449
  }
451
450
  this.files[index].status = 'success';
452
451
  this.files[index].progress += 1;
453
452
  successData.push(this.files[index]);
454
453
  tempFilePath.push(this.files[index].fileID);
455
454
  }
456
455
  }
457
456
  if (successData.length > 0) {
458
457
  this.setEmit();
459
458
  // 状态改变返回
460
459
  this.$emit('success', {
461
460
  tempFiles: this.backObject(successData),
462
461
  tempFilePaths: tempFilePath
463
462
  });
464
463
  }
465
464
  if (errorData.length > 0) {
466
465
  this.$emit('fail', {
467
466
  tempFiles: this.backObject(errorData),
468
467
  tempFilePaths: errorTempFilePath
469
468
  });
470
469
  }
471
470
  },
472
471
  /**
473
472
  * 获取进度
474
473
  * @param {Object} progressEvent
475
474
  * @param {Object} index
476
475
  * @param {Object} type
477
476
  */
478
477
  setProgress(progressEvent, index, type) {
479
478
  const fileLenth = this.files.length;
480
479
  const percentNum = (index / fileLenth) * 100;
481
480
  const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
482
481
  let idx = index;
483
482
  if (!type) {
484
483
  idx = this.files.findIndex((p) => p.uuid === progressEvent.tempFile.uuid);
485
484
  }
486
485
  if (idx === -1 || !this.files[idx]) return;
487
486
  // fix by mehaotian 100 就会消失,-1 是为了让进度条消失
488
487
  this.files[idx].progress = percentCompleted - 1;
489
488
  // 上传中
490
489
  this.$emit('progress', {
491
490
  index: idx,
492
491
  progress: parseInt(percentCompleted),
493
492
  tempFile: this.files[idx]
494
493
  });
495
494
  },
496
495
  /**
497
496
  * 删除文件
498
497
  * @param {Object} index
499
498
  */
500
499
  delFile(index) {
501
500
  this.$emit('delete', {
502
501
  tempFile: this.files[index],
503
502
  tempFilePath: this.files[index].url
504
503
  });
505
504
  this.files.splice(index, 1);
506
505
  this.$nextTick(() => {
507
506
  this.setEmit();
508
507
  });
509
508
  },
510
509
  /**
511
510
  * 获取文件名和后缀
512
511
  * @param {Object} name
513
512
  */
514
513
  getFileExt(name) {
515
514
  const last_len = name.lastIndexOf('.');
516
515
  const len = name.length;
517
516
  return {
518
517
  name: name.substring(0, last_len),
519
518
  ext: name.substring(last_len + 1, len)
520
519
  };
521
520
  },
522
521
  /**
523
522
  * 处理返回事件
524
523
  */
525
524
  setEmit() {
526
525
  let data = [];
527
526
  if (this.returnType === 'object') {
528
527
  data = this.backObject(this.files)[0];
529
528
  this.localValue = data ? data : null;
530
529
  } else {
531
530
  data = this.backObject(this.files);
532
531
  if (!this.localValue) {
533
532
  this.localValue = [];
534
533
  }
535
534
  this.localValue = [...data];
536
535
  }
537
536
  // #ifdef VUE3
538
537
  this.$emit('update:modelValue', this.localValue);
539
538
  // #endif
540
539
  // #ifndef VUE3
541
540
  this.$emit('input', this.localValue);
542
541
  // #endif
543
542
  },
544
543
  /**
545
544
  * 处理返回参数
546
545
  * @param {Object} files
547
546
  */
548
547
  backObject(files) {
549
548
  let newFilesData = [];
550
549
  files.forEach((v) => {
551
550
  newFilesData.push({
552
551
  extname: v.extname,
553
552
  fileType: v.fileType,
554
553
  image: v.image,
555
554
  name: v.name,
556
555
  path: v.path,
557
556
  size: v.size,
558
557
  fileID: v.fileID,
559
558
  url: v.url,
560
559
  // 修改删除一个文件后不能再上传的bug, #694
561
560
  uuid: v.uuid,
562
561
  status: v.status,
563
562
  cloudPath: v.cloudPath
564
563
  });
565
564
  });
566
565
  return newFilesData;
567
566
  },
568
567
  async getTempFileURL(fileList) {
569
568
  fileList = {
570
569
  fileList: [].concat(fileList)
571
570
  };
572
571
  const urls = await uniCloud.getTempFileURL(fileList);
573
572
  return urls.fileList[0].tempFileURL || '';
574
573
  },
575
574
  /**
576
575
  * 获取父元素实例
577
576
  */
578
577
  getForm(name = 'uniForms') {
579
578
  let parent = this.$parent;
580
579
  let parentName = parent.$options.name;
581
580
  while (parentName !== name) {
582
581
  parent = parent.$parent;
583
582
  if (!parent) return false;
584
583
  parentName = parent.$options.name;
585
584
  }
586
585
  return parent;
587
586
  }
588
587
  }
589
588
  /* #ifndef APP-NVUE */
590
589
  box-sizing: border-box;
591
590
  overflow: hidden;
592
591
  width: 100%;
593
592
  /* #endif */
594
593
  flex: 1;
595
594
  padding-top: 5px;
596
595
  padding-bottom: 10px;
597
596
  /* #ifndef APP-NVUE */
598
597
  display: flex;
599
598
  /* #endif */
600
599
  justify-content: space-between;
601
600
  font-size: 14px;
602
601
  color: #333;
603
602
  font-size: 14px;
604
603
  color: #999;
605
604
  /* #ifndef APP-NVUE */
606
605
  display: flex;
607
606
  /* #endif */
608
607
  align-items: center;
609
608
  justify-content: center;
610
609
  width: 50px;
611
610
  height: 5px;
612
611
  background-color: #f1f1f1;
613
612
  border-radius: 2px;
614
613
  position: absolute;
615
614
  transform: rotate(90deg);
615
+ <template>
616
+ <view class="uni-file-picker">
617
+ <view v-if="title" class="uni-file-picker__header">
618
+ <text class="file-title">{{ title }}</text>
619
+ <text class="file-count">{{ filesList.length }}/{{ limitLength }}</text>
620
+ </view>
621
+ <upload-image
622
+ v-if="fileMediatype === 'image' && showType === 'grid'"
623
+ :readonly="readonly"
624
+ :image-styles="imageStyles"
625
+ :files-list="filesList"
626
+ :limit="limitLength"
627
+ :disablePreview="disablePreview"
628
+ :delIcon="delIcon"
629
+ @uploadFiles="uploadFiles"
630
+ @choose="choose"
631
+ @delFile="delFile"
632
+ >
633
+ <slot>
634
+ <view class="is-add">
635
+ <view class="icon-add"></view>
636
+ <view class="icon-add rotate"></view>
637
+ </view>
638
+ </slot>
639
+ </upload-image>
640
+ <upload-file
641
+ v-if="fileMediatype !== 'image' || showType !== 'grid'"
642
+ :readonly="readonly"
643
+ :list-styles="listStyles"
644
+ :files-list="filesList"
645
+ :showType="showType"
646
+ :delIcon="delIcon"
647
+ @uploadFiles="uploadFiles"
648
+ @choose="choose"
649
+ @delFile="delFile"
650
+ >
651
+ <slot><button type="primary" size="mini">选择文件</button></slot>
652
+ </upload-file>
653
+ </view>
654
+ </template>
655
+ <script>
656
+ import { chooseAndUploadFile, uploadCloudFiles } from './choose-and-upload-file.js';
657
+ import { get_file_ext, get_extname, get_files_and_is_max, get_file_info, get_file_data } from './utils.js';
658
+ import uploadImage from './upload-image.vue';
659
+ import uploadFile from './upload-file.vue';
660
+ let fileInput = null;
661
+ /**
662
+ * FilePicker 文件选择上传
663
+ * @description 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
664
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=4079
665
+ * @property {Object|Array} value 组件数据,通常用来回显 ,类型由return-type属性决定
666
+ * @property {Boolean} disabled = [true|false] 组件禁用
667
+ * @value true 禁用
668
+ * @value false 取消禁用
669
+ * @property {Boolean} readonly = [true|false] 组件只读,不可选择,不显示进度,不显示删除按钮
670
+ * @value true 只读
671
+ * @value false 取消只读
672
+ * @property {String} return-type = [array|object] 限制 value 格式,当为 object 时 ,组件只能单选,且会覆盖
673
+ * @value array 规定 value 属性的类型为数组
674
+ * @value object 规定 value 属性的类型为对象
675
+ * @property {Boolean} disable-preview = [true|false] 禁用图片预览,仅 mode:grid 时生效
676
+ * @value true 禁用图片预览
677
+ * @value false 取消禁用图片预览
678
+ * @property {Boolean} del-icon = [true|false] 是否显示删除按钮
679
+ * @value true 显示删除按钮
680
+ * @value false 不显示删除按钮
681
+ * @property {Boolean} auto-upload = [true|false] 是否自动上传,值为true则只触发@select,可自行上传
682
+ * @value true 自动上传
683
+ * @value false 取消自动上传
684
+ * @property {Number|String} limit 最大选择个数 ,h5 会自动忽略多选的部分
685
+ * @property {String} title 组件标题,右侧显示上传计数
686
+ * @property {String} mode = [list|grid] 选择文件后的文件列表样式
687
+ * @value list 列表显示
688
+ * @value grid 宫格显示
689
+ * @property {String} file-mediatype = [image|video|all] 选择文件类型
690
+ * @value image 只选择图片
691
+ * @value video 只选择视频
692
+ * @value all 选择所有文件
693
+ * @property {Array} file-extname 选择文件后缀,根据 file-mediatype 属性而不同
694
+ * @property {Object} list-style mode:list 时的样式
695
+ * @property {Object} image-styles 选择文件后缀,根据 file-mediatype 属性而不同
696
+ * @event {Function} select 选择文件后触发
697
+ * @event {Function} progress 文件上传时触发
698
+ * @event {Function} success 上传成功触发
699
+ * @event {Function} fail 上传失败触发
700
+ * @event {Function} delete 文件从列表移除时触发
701
+ */
702
+ export default {
703
+ name: 'uniFilePicker',
704
+ components: {
705
+ uploadImage,
706
+ uploadFile
707
+ },
708
+ options: {
709
+ virtualHost: true
710
+ },
711
+ emits: ['select', 'success', 'fail', 'progress', 'delete', 'update:modelValue', 'input'],
712
+ props: {
713
+ // #ifdef VUE3
714
+ modelValue: {
715
+ type: [Array, Object],
716
+ default() {
717
+ return [];
718
+ }
719
+ },
720
+ // #endif
721
+ // #ifndef VUE3
722
+ value: {
723
+ type: [Array, Object],
724
+ default() {
725
+ return [];
726
+ }
727
+ },
728
+ // #endif
729
+ disabled: {
730
+ type: Boolean,
731
+ default: false
732
+ },
733
+ disablePreview: {
734
+ type: Boolean,
735
+ default: false
736
+ },
737
+ delIcon: {
738
+ type: Boolean,
739
+ default: true
740
+ },
741
+ // 自动上传
742
+ autoUpload: {
743
+ type: Boolean,
744
+ default: true
745
+ },
746
+ // 最大选择个数 ,h5只能限制单选或是多选
747
+ limit: {
748
+ type: [Number, String],
749
+ default: 9
750
+ },
751
+ // 列表样式 grid | list | list-card
752
+ mode: {
753
+ type: String,
754
+ default: 'grid'
755
+ },
756
+ // 选择文件类型 image/video/all
757
+ fileMediatype: {
758
+ type: String,
759
+ default: 'image'
760
+ },
761
+ // 文件类型筛选
762
+ fileExtname: {
763
+ type: [Array, String],
764
+ default() {
765
+ return [];
766
+ }
767
+ },
768
+ title: {
769
+ type: String,
770
+ default: ''
771
+ },
772
+ listStyles: {
773
+ type: Object,
774
+ default() {
775
+ return {
776
+ // 是否显示边框
777
+ border: true,
778
+ // 是否显示分隔线
779
+ dividline: true,
780
+ // 线条样式
781
+ borderStyle: {}
782
+ };
783
+ }
784
+ },
785
+ imageStyles: {
786
+ type: Object,
787
+ default() {
788
+ return {
789
+ width: 'auto',
790
+ height: 'auto'
791
+ };
792
+ }
793
+ },
794
+ readonly: {
795
+ type: Boolean,
796
+ default: false
797
+ },
798
+ returnType: {
799
+ type: String,
800
+ default: 'array'
801
+ },
802
+ sizeType: {
803
+ type: Array,
804
+ default() {
805
+ return ['original', 'compressed'];
806
+ }
807
+ },
808
+ sourceType: {
809
+ type: Array,
810
+ default() {
811
+ return ['album', 'camera'];
812
+ }
813
+ }
814
+ },
815
+ data() {
816
+ return {
817
+ files: [],
818
+ localValue: []
819
+ };
820
+ },
821
+ watch: {
822
+ // #ifndef VUE3
823
+ value: {
824
+ handler(newVal, oldVal) {
825
+ this.setValue(newVal, oldVal);
826
+ },
827
+ immediate: true
828
+ },
829
+ // #endif
830
+ // #ifdef VUE3
831
+ modelValue: {
832
+ handler(newVal, oldVal) {
833
+ this.setValue(newVal, oldVal);
834
+ },
835
+ immediate: true
836
+ }
837
+ // #endif
838
+ },
839
+ computed: {
840
+ filesList() {
841
+ let files = [];
842
+ this.files.forEach((v) => {
843
+ files.push(v);
844
+ });
845
+ return files;
846
+ },
847
+ showType() {
848
+ if (this.fileMediatype === 'image') {
849
+ return this.mode;
850
+ }
851
+ return 'list';
852
+ },
853
+ limitLength() {
854
+ if (this.returnType === 'object') {
855
+ return 1;
856
+ }
857
+ if (!this.limit) {
858
+ return 1;
859
+ }
860
+ if (this.limit >= 9) {
861
+ return 9;
862
+ }
863
+ return this.limit;
864
+ }
865
+ },
866
+ created() {
867
+ // TODO 兼容不开通服务空间的情况
868
+ if (!(uniCloud.config && uniCloud.config.provider)) {
869
+ this.noSpace = true;
870
+ uniCloud.chooseAndUploadFile = chooseAndUploadFile;
871
+ }
872
+ this.form = this.getForm('uniForms');
873
+ this.formItem = this.getForm('uniFormsItem');
874
+ if (this.form && this.formItem) {
875
+ if (this.formItem.name) {
876
+ this.rename = this.formItem.name;
877
+ this.form.inputChildrens.push(this);
878
+ }
879
+ }
880
+ },
881
+ methods: {
882
+ /**
883
+ * 公开用户使用,清空文件
884
+ * @param {Object} index
885
+ */
886
+ clearFiles(index) {
887
+ if (index !== 0 && !index) {
888
+ this.files = [];
889
+ this.$nextTick(() => {
890
+ this.setEmit();
891
+ });
892
+ } else {
893
+ this.files.splice(index, 1);
894
+ }
895
+ this.$nextTick(() => {
896
+ this.setEmit();
897
+ });
898
+ },
899
+ /**
900
+ * 公开用户使用,继续上传
901
+ */
902
+ upload() {
903
+ let files = [];
904
+ this.files.forEach((v, index) => {
905
+ if (v.status === 'ready' || v.status === 'error') {
906
+ files.push(Object.assign({}, v));
907
+ }
908
+ });
909
+ return this.uploadFiles(files);
910
+ },
911
+ async setValue(newVal, oldVal) {
912
+ const newData = async (v) => {
913
+ const reg = /cloud:\/\/([\w.]+\/?)\S*/;
914
+ let url = '';
915
+ if (v.fileID) {
916
+ url = v.fileID;
917
+ } else {
918
+ url = v.url;
919
+ }
920
+ if (reg.test(url)) {
921
+ v.fileID = url;
922
+ v.url = await this.getTempFileURL(url);
923
+ }
924
+ if (v.url) v.path = v.url;
925
+ return v;
926
+ };
927
+ if (this.returnType === 'object') {
928
+ if (newVal) {
929
+ await newData(newVal);
930
+ } else {
931
+ newVal = {};
932
+ }
933
+ } else {
934
+ if (!newVal) newVal = [];
935
+ for (let i = 0; i < newVal.length; i++) {
936
+ let v = newVal[i];
937
+ await newData(v);
938
+ }
939
+ }
940
+ this.localValue = newVal;
941
+ if (this.form && this.formItem && !this.is_reset) {
942
+ this.is_reset = false;
943
+ this.formItem.setValue(this.localValue);
944
+ }
945
+ let filesData = Object.keys(newVal).length > 0 ? newVal : [];
946
+ this.files = [].concat(filesData);
947
+ },
948
+ /**
949
+ * 选择文件
950
+ */
951
+ choose() {
952
+ if (this.disabled) return;
953
+ if (this.files.length >= Number(this.limitLength) && this.showType !== 'grid' && this.returnType === 'array') {
954
+ uni.showToast({
955
+ title: `您最多选择 ${this.limitLength} 个文件`,
956
+ icon: 'none'
957
+ });
958
+ return;
959
+ }
960
+ this.chooseFiles();
961
+ },
962
+ /**
963
+ * 选择文件并上传
964
+ */
965
+ chooseFiles() {
966
+ const _extname = get_extname(this.fileExtname);
967
+ // 获取后缀
968
+ uniCloud
969
+ .chooseAndUploadFile({
970
+ type: this.fileMediatype,
971
+ compressed: false,
972
+ sizeType: this.sizeType,
973
+ sourceType: this.sourceType,
974
+ // TODO 如果为空,video 有问题
975
+ extension: _extname.length > 0 ? _extname : undefined,
976
+ count: this.limitLength - this.files.length, //默认9
977
+ onChooseFile: this.chooseFileCallback,
978
+ onUploadProgress: (progressEvent) => {
979
+ this.setProgress(progressEvent, progressEvent.index);
980
+ }
981
+ })
982
+ .then((result) => {
983
+ this.setSuccessAndError(result.tempFiles);
984
+ })
985
+ .catch((err) => {
986
+ console.log('选择失败', err);
987
+ });
988
+ },
989
+ /**
990
+ * 选择文件回调
991
+ * @param {Object} res
992
+ */
993
+ async chooseFileCallback(res) {
994
+ const _extname = get_extname(this.fileExtname);
995
+ const is_one = (Number(this.limitLength) === 1 && this.disablePreview && !this.disabled) || this.returnType === 'object';
996
+ // 如果这有一个文件 ,需要清空本地缓存数据
997
+ if (is_one) {
998
+ this.files = [];
999
+ }
1000
+ let { filePaths, files } = get_files_and_is_max(res, _extname);
1001
+ if (!(_extname && _extname.length > 0)) {
1002
+ filePaths = res.tempFilePaths;
1003
+ files = res.tempFiles;
1004
+ }
1005
+ let currentData = [];
1006
+ for (let i = 0; i < files.length; i++) {
1007
+ if (this.limitLength - this.files.length <= 0) break;
1008
+ files[i].uuid = Date.now();
1009
+ let filedata = await get_file_data(files[i], this.fileMediatype);
1010
+ filedata.progress = 0;
1011
+ filedata.status = 'ready';
1012
+ this.files.push(filedata);
1013
+ currentData.push({
1014
+ ...filedata,
1015
+ file: files[i]
1016
+ });
1017
+ }
1018
+ this.$emit('select', {
1019
+ tempFiles: currentData,
1020
+ tempFilePaths: filePaths
1021
+ });
1022
+ res.tempFiles = files;
1023
+ // 停止自动上传
1024
+ if (!this.autoUpload || this.noSpace) {
1025
+ res.tempFiles = [];
1026
+ }
1027
+ },
1028
+ /**
1029
+ * 批传
1030
+ * @param {Object} e
1031
+ */
1032
+ uploadFiles(files) {
1033
+ files = [].concat(files);
1034
+ return uploadCloudFiles
1035
+ .call(this, files, 5, (res) => {
1036
+ this.setProgress(res, res.index, true);
1037
+ })
1038
+ .then((result) => {
1039
+ this.setSuccessAndError(result);
1040
+ return result;
1041
+ })
1042
+ .catch((err) => {
1043
+ console.log(err);
1044
+ });
1045
+ },
1046
+ /**
1047
+ * 成功或失败
1048
+ */
1049
+ async setSuccessAndError(res, fn) {
1050
+ let successData = [];
1051
+ let errorData = [];
1052
+ let tempFilePath = [];
1053
+ let errorTempFilePath = [];
1054
+ for (let i = 0; i < res.length; i++) {
1055
+ const item = res[i];
1056
+ const index = item.uuid ? this.files.findIndex((p) => p.uuid === item.uuid) : item.index;
1057
+ if (index === -1 || !this.files) break;
1058
+ if (item.errMsg === 'request:fail') {
1059
+ this.files[index].url = item.path;
1060
+ this.files[index].status = 'error';
1061
+ this.files[index].errMsg = item.errMsg;
1062
+ // this.files[index].progress = -1
1063
+ errorData.push(this.files[index]);
1064
+ errorTempFilePath.push(this.files[index].url);
1065
+ } else {
1066
+ this.files[index].errMsg = '';
1067
+ this.files[index].fileID = item.url;
1068
+ const reg = /cloud:\/\/([\w.]+\/?)\S*/;
1069
+ if (reg.test(item.url)) {
1070
+ this.files[index].url = await this.getTempFileURL(item.url);
1071
+ } else {
1072
+ this.files[index].url = item.url;
1073
+ }
1074
+ this.files[index].status = 'success';
1075
+ this.files[index].progress += 1;
1076
+ successData.push(this.files[index]);
1077
+ tempFilePath.push(this.files[index].fileID);
1078
+ }
1079
+ }
1080
+ if (successData.length > 0) {
1081
+ this.setEmit();
1082
+ // 状态改变返回
1083
+ this.$emit('success', {
1084
+ tempFiles: this.backObject(successData),
1085
+ tempFilePaths: tempFilePath
1086
+ });
1087
+ }
1088
+ if (errorData.length > 0) {
1089
+ this.$emit('fail', {
1090
+ tempFiles: this.backObject(errorData),
1091
+ tempFilePaths: errorTempFilePath
1092
+ });
1093
+ }
1094
+ },
1095
+ /**
1096
+ * 获取进度
1097
+ * @param {Object} progressEvent
1098
+ * @param {Object} index
1099
+ * @param {Object} type
1100
+ */
1101
+ setProgress(progressEvent, index, type) {
1102
+ const fileLenth = this.files.length;
1103
+ const percentNum = (index / fileLenth) * 100;
1104
+ const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
1105
+ let idx = index;
1106
+ if (!type) {
1107
+ idx = this.files.findIndex((p) => p.uuid === progressEvent.tempFile.uuid);
1108
+ }
1109
+ if (idx === -1 || !this.files[idx]) return;
1110
+ // fix by mehaotian 100 就会消失,-1 是为了让进度条消失
1111
+ this.files[idx].progress = percentCompleted - 1;
1112
+ // 上传中
1113
+ this.$emit('progress', {
1114
+ index: idx,
1115
+ progress: parseInt(percentCompleted),
1116
+ tempFile: this.files[idx]
1117
+ });
1118
+ },
1119
+ /**
1120
+ * 删除文件
1121
+ * @param {Object} index
1122
+ */
1123
+ delFile(index) {
1124
+ this.$emit('delete', {
1125
+ tempFile: this.files[index],
1126
+ tempFilePath: this.files[index].url
1127
+ });
1128
+ this.files.splice(index, 1);
1129
+ this.$nextTick(() => {
1130
+ this.setEmit();
1131
+ });
1132
+ },
1133
+ /**
1134
+ * 获取文件名和后缀
1135
+ * @param {Object} name
1136
+ */
1137
+ getFileExt(name) {
1138
+ const last_len = name.lastIndexOf('.');
1139
+ const len = name.length;
1140
+ return {
1141
+ name: name.substring(0, last_len),
1142
+ ext: name.substring(last_len + 1, len)
1143
+ };
1144
+ },
1145
+ /**
1146
+ * 处理返回事件
1147
+ */
1148
+ setEmit() {
1149
+ let data = [];
1150
+ if (this.returnType === 'object') {
1151
+ data = this.backObject(this.files)[0];
1152
+ this.localValue = data ? data : null;
1153
+ } else {
1154
+ data = this.backObject(this.files);
1155
+ if (!this.localValue) {
1156
+ this.localValue = [];
1157
+ }
1158
+ this.localValue = [...data];
1159
+ }
1160
+ // #ifdef VUE3
1161
+ this.$emit('update:modelValue', this.localValue);
1162
+ // #endif
1163
+ // #ifndef VUE3
1164
+ this.$emit('input', this.localValue);
1165
+ // #endif
1166
+ },
1167
+ /**
1168
+ * 处理返回参数
1169
+ * @param {Object} files
1170
+ */
1171
+ backObject(files) {
1172
+ let newFilesData = [];
1173
+ files.forEach((v) => {
1174
+ newFilesData.push({
1175
+ extname: v.extname,
1176
+ fileType: v.fileType,
1177
+ image: v.image,
1178
+ name: v.name,
1179
+ path: v.path,
1180
+ size: v.size,
1181
+ fileID: v.fileID,
1182
+ url: v.url,
1183
+ // 修改删除一个文件后不能再上传的bug, #694
1184
+ uuid: v.uuid,
1185
+ status: v.status,
1186
+ cloudPath: v.cloudPath
1187
+ });
1188
+ });
1189
+ return newFilesData;
1190
+ },
1191
+ async getTempFileURL(fileList) {
1192
+ fileList = {
1193
+ fileList: [].concat(fileList)
1194
+ };
1195
+ const urls = await uniCloud.getTempFileURL(fileList);
1196
+ return urls.fileList[0].tempFileURL || '';
1197
+ },
1198
+ /**
1199
+ * 获取父元素实例
1200
+ */
1201
+ getForm(name = 'uniForms') {
1202
+ let parent = this.$parent;
1203
+ let parentName = parent.$options.name;
1204
+ while (parentName !== name) {
1205
+ parent = parent.$parent;
1206
+ if (!parent) return false;
1207
+ parentName = parent.$options.name;
1208
+ }
1209
+ return parent;
1210
+ }
1211
+ }
1212
+ };
1213
+ </script>
1214
+ <style>
1215
+ .uni-file-picker {
1216
+ /* #ifndef APP-NVUE */
1217
+ box-sizing: border-box;
1218
+ overflow: hidden;
1219
+ width: 100%;
1220
+ /* #endif */
1221
+ flex: 1;
1222
+ }
1223
+ .uni-file-picker__header {
1224
+ padding-top: 5px;
1225
+ padding-bottom: 10px;
1226
+ /* #ifndef APP-NVUE */
1227
+ display: flex;
1228
+ /* #endif */
1229
+ justify-content: space-between;
1230
+ }
1231
+ .file-title {
1232
+ font-size: 14px;
1233
+ color: #333;
1234
+ }
1235
+ .file-count {
1236
+ font-size: 14px;
1237
+ color: #999;
1238
+ }
1239
+ .is-add {
1240
+ /* #ifndef APP-NVUE */
1241
+ display: flex;
1242
+ /* #endif */
1243
+ align-items: center;
1244
+ justify-content: center;
1245
+ }
1246
+ .icon-add {
1247
+ width: 50px;
1248
+ height: 5px;
1249
+ background-color: #f1f1f1;
1250
+ border-radius: 2px;
1251
+ }
1252
+ .rotate {
1253
+ position: absolute;
1254
+ transform: rotate(90deg);
1255
+ }
1256
+ </style>