@ecolovo/uniapp-components 1.0.3 → 1.0.5

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.
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <view :class="componentClassNames">
3
3
  <view :class="$style['Aspect--content-wrapper']" :style="{ paddingTop: 100 / ratio + '%' }">
4
- <view :class="$style['Aspect--content']">
4
+ <view :class="$style['Aspect--content']" @click="$emit('click')">
5
5
  <slot></slot>
6
6
  </view>
7
7
  </view>
@@ -21,7 +21,7 @@
21
21
  ]"
22
22
  :hover-class="disabled ? 'none' : $style['Button--active']"
23
23
  :style="buttonTheme"
24
- @click.stop="onClick"
24
+ @click="onClick"
25
25
  @getphonenumber="onOpenType('getphonenumber', $event)"
26
26
  @agreeprivacyauthorization="onOpenType('agreeprivacyauthorization', $event)"
27
27
  >
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <view :class="componentClassNames" @click="toggle()">
3
3
  <icon-cpn
4
+ v-if="checkboxGroupProps.icon"
4
5
  :icon="usingIcon || ''"
5
6
  tap-size="50rpx"
6
7
  :class="[$style['Checkbox--icon'], { [$style['Checkbox--checked']]: isChecked }]"
@@ -42,7 +43,7 @@ export default {
42
43
  },
43
44
  },
44
45
  props: {
45
- /** 值 */
46
+ /** 当在 checkbox-group 中时,这个值用于选项值;否则用于 v-model ,只能是 boolean 类型 */
46
47
  value: {
47
48
  type: [Number, String, Boolean, null],
48
49
  default: null,
@@ -114,16 +115,16 @@ export default {
114
115
  }
115
116
  return this.value === null ? this.innerChecked : this.value;
116
117
  },
117
- set(value) {
118
- this.$emit('input', value);
119
- this.innerChecked = value;
120
- this.$emit('change', this.innerChecked);
118
+ set(val) {
119
+ this.$emit('input', val);
120
+ this.innerChecked = val;
121
+ this.$emit('change', val);
121
122
  },
122
123
  },
123
124
  },
124
125
  methods: {
125
126
  /** 切换选择 */
126
- toggle(isChecked) {
127
+ toggle(checked) {
127
128
  if (this.isReadonly) return;
128
129
 
129
130
  if (this.isInGroup) {
@@ -131,13 +132,13 @@ export default {
131
132
  return;
132
133
  }
133
134
 
134
- if (isChecked === undefined || isChecked === null) {
135
+ if (checked === undefined || checked === null) {
135
136
  this.isChecked = !this.isChecked;
136
137
  return;
137
138
  }
138
139
 
139
- if (this.isChecked !== !!isChecked) {
140
- this.isChecked = !!isChecked;
140
+ if (this.isChecked !== !!checked) {
141
+ this.isChecked = !!checked;
141
142
  }
142
143
  },
143
144
  },
@@ -167,6 +168,7 @@ $component-vars: $checkbox;
167
168
  }
168
169
  .Checkbox--icon {
169
170
  flex: none;
171
+ margin-right: 12rpx;
170
172
  color: UTILS__LOCAL_VAR(unchecked-color);
171
173
  }
172
174
  .Checkbox--checked {
@@ -177,7 +179,6 @@ $component-vars: $checkbox;
177
179
  }
178
180
  .Checkbox--text {
179
181
  flex: 0 1 auto;
180
- margin-left: 12rpx;
181
182
  }
182
183
  }
183
184
  </style>
@@ -32,6 +32,11 @@ export default {
32
32
  type: Boolean,
33
33
  default: false,
34
34
  },
35
+ /** 是否显示勾选图标 */
36
+ icon: {
37
+ type: Boolean,
38
+ default: true,
39
+ },
35
40
  /** 选中时的图标 */
36
41
  checkedIcon: {
37
42
  type: String,
@@ -53,37 +58,38 @@ export default {
53
58
  get: function () {
54
59
  return this.value === null ? this.checkedValue : this.value;
55
60
  },
56
- set: function (value) {
57
- this.$emit('input', value);
58
- this.checkedValue = value;
59
- this.$emit('change', this.checkedValue);
61
+ set: function (val) {
62
+ this.$emit('input', val);
63
+ this.checkedValue = val;
64
+ this.$emit('change', val);
60
65
  },
61
66
  },
62
67
  },
63
68
  watch: {
64
69
  value: {
65
- handler: function (value) {
66
- this.checkedValue = value;
70
+ handler: function (val) {
71
+ this.checkedValue = val;
67
72
  },
68
73
  immediate: true,
74
+ deep: true,
69
75
  },
70
76
  },
71
77
  methods: {
72
- check(value) {
78
+ check(val) {
73
79
  if (this.radio) {
74
- this.currentValue = value;
80
+ this.currentValue = val;
75
81
  return;
76
82
  }
77
83
 
78
84
  if (!Array.isArray(this.currentValue)) {
79
- this.currentValue = [value];
85
+ this.currentValue = [val];
80
86
  return;
81
87
  }
82
88
 
83
- if (this.currentValue.includes(value)) {
84
- this.currentValue = this.currentValue.filter((item) => item !== value);
89
+ if (this.currentValue.includes(val)) {
90
+ this.currentValue = this.currentValue.filter((item) => item !== val);
85
91
  } else {
86
- this.currentValue.push(value);
92
+ this.currentValue.push(val);
87
93
  }
88
94
  },
89
95
  },
@@ -4,6 +4,7 @@
4
4
  v-for="(item, index) in options"
5
5
  :key="index"
6
6
  :value="radio ? item.value === currentValue : currentValue && currentValue.includes(item.value)"
7
+ :icon="icon"
7
8
  :checked-icon="checkedIcon || (radio ? 'radio-checked' : 'checkbox-checked')"
8
9
  :unchecked-icon="uncheckedIcon || (radio ? 'radio-unchecked' : 'checkbox-unchecked')"
9
10
  :class="$style['CheckboxGroup2--item']"
@@ -42,6 +43,11 @@ export default {
42
43
  type: Boolean,
43
44
  default: true,
44
45
  },
46
+ /** 是否显示勾选图标 */
47
+ icon: {
48
+ type: Boolean,
49
+ default: true,
50
+ },
45
51
  /** 选中时的图标 */
46
52
  checkedIcon: {
47
53
  type: String,
@@ -63,10 +69,10 @@ export default {
63
69
  get() {
64
70
  return this.value === null ? this.checkedValue : this.value;
65
71
  },
66
- set(value) {
67
- this.$emit('input', value);
68
- this.checkedValue = value;
69
- this.$emit('change', this.checkedValue);
72
+ set(val) {
73
+ this.$emit('input', val);
74
+ this.checkedValue = val;
75
+ this.$emit('change', val);
70
76
  },
71
77
  },
72
78
  optionsValue() {
@@ -74,21 +80,21 @@ export default {
74
80
  },
75
81
  },
76
82
  methods: {
77
- check(value) {
83
+ check(val) {
78
84
  if (this.radio) {
79
- this.currentValue = value;
85
+ this.currentValue = val;
80
86
  return;
81
87
  }
82
88
 
83
89
  if (!Array.isArray(this.currentValue)) {
84
- this.currentValue = [value];
90
+ this.currentValue = [val];
85
91
  return;
86
92
  }
87
93
 
88
- if (this.currentValue.includes(value)) {
89
- this.currentValue = this.currentValue.filter((item) => item !== value);
94
+ if (this.currentValue.includes(val)) {
95
+ this.currentValue = this.currentValue.filter((item) => item !== val);
90
96
  } else {
91
- this.currentValue.push(value);
97
+ this.currentValue.push(val);
92
98
  }
93
99
  },
94
100
  /** 切换全选
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <view :class="componentClassNames" @click="toggle()">
3
3
  <icon-cpn
4
+ v-if="icon"
4
5
  :icon="currentValue ? checkedIcon || 'checkbox-checked' : uncheckedIcon || 'checkbox-unchecked'"
5
6
  tap-size="50rpx"
6
7
  :class="[$style['Checkbox2--icon'], { [$style['Checkbox2--checked']]: currentValue }]"
@@ -29,6 +30,11 @@ export default {
29
30
  type: [Boolean, null],
30
31
  default: null,
31
32
  },
33
+ /** 是否显示勾选图标 */
34
+ icon: {
35
+ type: Boolean,
36
+ default: true,
37
+ },
32
38
  /** 选中时的图标 */
33
39
  checkedIcon: {
34
40
  type: String,
@@ -50,23 +56,23 @@ export default {
50
56
  get: function () {
51
57
  return this.value === null ? this.innerValue : this.value;
52
58
  },
53
- set: function (value) {
54
- this.$emit('input', value);
55
- this.innerValue = value;
56
- this.$emit('change', this.innerValue);
59
+ set: function (val) {
60
+ this.$emit('input', val);
61
+ this.innerValue = val;
62
+ this.$emit('change', val);
57
63
  },
58
64
  },
59
65
  },
60
66
  methods: {
61
67
  /** 切换选择 */
62
- toggle(isSelected) {
63
- if (isSelected === undefined || isSelected === null) {
68
+ toggle(checked) {
69
+ if (checked === undefined || checked === null) {
64
70
  this.currentValue = !this.currentValue;
65
71
  return;
66
72
  }
67
73
 
68
- if (this.currentValue !== !!isSelected) {
69
- this.currentValue = !!isSelected;
74
+ if (this.currentValue !== !!checked) {
75
+ this.currentValue = !!checked;
70
76
  }
71
77
  },
72
78
  },
@@ -90,6 +96,7 @@ $component-vars: $checkbox;
90
96
 
91
97
  .Checkbox2--icon {
92
98
  flex: none;
99
+ margin-right: 12rpx;
93
100
  color: UTILS__LOCAL_VAR(unchecked-color);
94
101
  }
95
102
  .Checkbox2--checked {
@@ -97,7 +104,6 @@ $component-vars: $checkbox;
97
104
  }
98
105
  .Checkbox2--text {
99
106
  flex: 0 1 auto;
100
- margin-left: 12rpx;
101
107
  }
102
108
  }
103
109
  </style>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <view :class="componentClassNames" :style="{ '--columns': columns }">
3
3
  <!-- 已上传文件列表 -->
4
- <aspect-cpn v-for="(item, idx) in assetsList" :key="idx" :class="$style['Uploader--item']" :style="styleWidth">
4
+ <aspect-cpn v-for="(item, idx) in fileList" :key="idx" :class="$style['Uploader--item']" :style="styleWidth">
5
5
  <!-- TODO: 视频文件支持 -->
6
6
  <!-- <video
7
7
  v-if="item.fileType === 'video'"
@@ -24,21 +24,30 @@
24
24
  :style="styleSize"
25
25
  mode="aspectFill"
26
26
  />
27
+ <loading-cpn
28
+ v-if="!isReadonly && item.uploadStatus === 'uploading'"
29
+ :class="$style['Uploader--upload-loading']"
30
+ />
31
+ <view v-if="!isReadonly && item.uploadStatus === 'error'" :class="$style['Uploader--upload-error']">
32
+ 上传失败
33
+ </view>
27
34
  <view
28
- v-if="item.uploadStatus !== 'uploading'"
35
+ v-if="!isReadonly && item.uploadStatus !== 'uploading'"
29
36
  :class="$style['Uploader--icon-delete']"
30
37
  @click.stop="deleteFile(idx)"
31
38
  >
32
39
  </view>
33
40
  </aspect-cpn>
34
41
  <!-- 上传按钮 -->
35
- <aspect-cpn :class="$style['Uploader--item']" :style="styleWidth">
36
- <button-cpn plain :class="$style['Uploader--button']" @click="selectFile">
37
- <view style="line-height: 1.3em">
38
- <view>上传</view>
39
- <view>图片</view>
40
- </view>
41
- </button-cpn>
42
+ <aspect-cpn v-if="!isReadonly" :class="$style['Uploader--item']" :style="styleWidth" @click="selectFile">
43
+ <slot>
44
+ <button-cpn plain :class="$style['Uploader--button']">
45
+ <view style="line-height: 1.3em">
46
+ <view>上传</view>
47
+ <view>图片</view>
48
+ </view>
49
+ </button-cpn>
50
+ </slot>
42
51
  </aspect-cpn>
43
52
  </view>
44
53
  </template>
@@ -48,6 +57,7 @@ import componentName from '../../mixins/componentName';
48
57
  import AspectCpn from '../aspect/aspect.vue';
49
58
  import ButtonCpn from '../button/button.vue';
50
59
  import ImageCpn from '../image/image.vue';
60
+ import LoadingCpn from '../loading/animations/loading-animation4.vue';
51
61
  import UploadMixin from './mixin';
52
62
 
53
63
  export default {
@@ -56,7 +66,7 @@ export default {
56
66
  virtualHost: true,
57
67
  styleIsolation: 'shared',
58
68
  },
59
- components: { AspectCpn, ButtonCpn, ImageCpn },
69
+ components: { AspectCpn, ButtonCpn, ImageCpn, LoadingCpn },
60
70
  mixins: [componentName, UploadMixin],
61
71
  props: {
62
72
  /** 显示宽度 */
@@ -78,7 +88,7 @@ export default {
78
88
  },
79
89
  /** 预览列表 */
80
90
  previewList() {
81
- return this.assetsList.map((item) => item.tempFilePath || item.url);
91
+ return this.fileList.map((item) => item.tempFilePath || item.url);
82
92
  },
83
93
  },
84
94
  methods: {
@@ -88,7 +98,10 @@ export default {
88
98
  mediaType: ['image', 'video'],
89
99
  sourceType: ['album', 'camera'],
90
100
  success: (res) => {
91
- this.validateFiles(res.tempFiles);
101
+ const files = this.validateFiles(res.tempFiles);
102
+ if (this.autoUpload && files) {
103
+ this.upload();
104
+ }
92
105
  },
93
106
  });
94
107
  },
@@ -105,11 +118,39 @@ export default {
105
118
  }
106
119
 
107
120
  .Uploader--item {
121
+ position: relative;
108
122
  width: calc(100% / var(--columns) - 12rpx * (var(--columns) - 1) / var(--columns));
109
123
  }
110
124
  .Uploader--image {
111
125
  border-radius: 16rpx;
112
126
  }
127
+ .Uploader--upload-loading {
128
+ pointer-events: none;
129
+ position: absolute;
130
+ top: 50%;
131
+ left: 50%;
132
+ font-size: 2em;
133
+ color: #fff;
134
+ transform: translate(-50%, -50%);
135
+ }
136
+ .Uploader--upload-error {
137
+ pointer-events: none;
138
+ position: absolute;
139
+ top: 50%;
140
+ left: 50%;
141
+ width: 2em;
142
+ height: 2em;
143
+ box-sizing: content-box;
144
+ transform: translate(-50%, -50%);
145
+ padding: 1.25em;
146
+ border-radius: 50%;
147
+ background-color: #00000080;
148
+ color: #fff;
149
+ font-size: 22rpx;
150
+ display: flex;
151
+ justify-content: center;
152
+ align-items: center;
153
+ }
113
154
  .Uploader--button {
114
155
  height: 100%;
115
156
  border-color: #ccc;
@@ -1,18 +1,40 @@
1
+ import { fileSize } from '../../utils/fileSize';
2
+ import { isFormReadonly } from '../form/mixin';
3
+
1
4
  export default {
5
+ mixins: [isFormReadonly],
2
6
  props: {
3
- /** 文件列表 */
7
+ /** 绑定值,文件列表 */
4
8
  value: {
5
9
  type: Array,
6
10
  default: () => [],
7
11
  },
12
+ /** 是否只读 */
13
+ readonly: {
14
+ type: Boolean,
15
+ default: false,
16
+ },
17
+ /** 最大上传文件数 */
8
18
  max: {
9
19
  type: Number,
10
20
  default: 9,
11
21
  },
22
+ /** 最大上传文件大小 */
12
23
  maxSize: {
13
24
  type: Number,
14
25
  default: 1024 * 1024 * 10, // 10MB
15
26
  },
27
+ /** 自动上传文件 */
28
+ autoUpload: {
29
+ type: Boolean,
30
+ default: true,
31
+ },
32
+ /** 上传文件方法
33
+ *
34
+ * 函数签名:
35
+ * @param {UniApp.MediaFile} file - 待上传文件
36
+ * @return {Promise<{url: string}>} - 上传后的文件信息
37
+ */
16
38
  uploadHandler: {
17
39
  type: Function,
18
40
  default: null,
@@ -20,79 +42,122 @@ export default {
20
42
  },
21
43
  data() {
22
44
  return {
23
- innerValue: [],
45
+ innerValue: this.value,
24
46
  };
25
47
  },
26
48
  computed: {
27
- currentValue: {
28
- get: function () {
29
- return this.value === null ? this.innerValue : this.value;
30
- },
31
- set: function (value) {
32
- this.$emit('input', value);
33
- this.innerValue = value;
34
- this.$emit('change', value);
35
- },
49
+ isReadonly() {
50
+ return this.readonly || this.isFormReadonly;
36
51
  },
52
+ /** 剩余可上传文件数 */
37
53
  restCount() {
38
54
  if (this.max <= 0) return 9;
39
55
  return this.max - this.currentValue.length;
40
56
  },
41
- assetsList() {
57
+ currentValue: {
58
+ get: function () {
59
+ return this.innerValue;
60
+ },
61
+ set: function (val) {
62
+ this.innerValue = val;
63
+ this.$emit('input', val);
64
+ this.$emit('change', val);
65
+ },
66
+ },
67
+ fileList() {
42
68
  return this.currentValue.map((item) =>
43
69
  typeof item === 'string' ? { url: item, uploadStatus: 'success' } : { uploadStatus: 'success', ...item }
44
70
  );
45
71
  },
72
+ /** 当前是否有文件还在上传 */
73
+ isUploading() {
74
+ return this.currentValue.some((item) => item.uploadStatus === 'uploading');
75
+ },
76
+ },
77
+ watch: {
78
+ value: {
79
+ handler(val) {
80
+ this.innerValue = val || [];
81
+ },
82
+ immediate: true,
83
+ deep: true,
84
+ },
46
85
  },
47
86
  methods: {
48
87
  /** 校验待上传文件 */
49
- async validateFiles(files) {
88
+ validateFiles(files) {
50
89
  if (files.length === 0) return;
51
90
 
52
91
  // 过滤大文件
53
- const validFiles = files.filter((file) => file.size <= this.maxSize);
54
- if (validFiles.length < files.length) {
55
- uni.showToast({ icon: 'none', title: '已过滤超出大小限制的文件', duration: 2000 });
92
+ const selectedFiles = files.filter((file) => file.size <= this.maxSize);
93
+ if (selectedFiles.length < files.length) {
94
+ uni.showToast({
95
+ icon: 'none',
96
+ title: `文件大小不能超过${fileSize(this.maxSize).formattedSize}`,
97
+ duration: 2000,
98
+ });
56
99
  }
57
- if (validFiles.length === 0) return;
100
+ if (selectedFiles.length === 0) return;
58
101
 
59
102
  // 校验数量
60
- if (validFiles.length > this.restCount) {
103
+ if (selectedFiles.length > this.restCount) {
61
104
  uni.showToast({ icon: 'none', title: `最多只能上传${this.max}个文件`, duration: 2000 });
62
105
  }
63
- this.currentValue = this.currentValue.concat(
64
- validFiles.slice(0, this.restCount).map((file) => ({ ...file, uploadStatus: 'ready' }))
65
- );
106
+
107
+ const validFiles = selectedFiles
108
+ .slice(0, this.restCount)
109
+ .map((file) => ({ ...file, uploadStatus: 'ready' }));
110
+ this.currentValue = this.fileList.concat(validFiles);
111
+
112
+ return validFiles;
66
113
  },
67
114
  /** 上传文件 */
68
- upload(files) {
69
- files.map(async (file) => {
70
- try {
71
- const res = await this.uploadHandler(file);
72
- const index = this.currentValue.findIndex((item) => item.tempFilePath === file.tempFilePath);
73
- if (index < 0) {
74
- return;
75
- }
76
- const currentValue = [...this.currentValue];
77
- currentValue.splice(index, 1, {
78
- fileId: res.fileId,
79
- fileName: res.fileName,
80
- filePreviewUrl: res.filePreviewUrl,
81
- fileUrl: res.fileUrl,
82
- fileType: file.type,
83
- });
84
- this.currentValue = currentValue;
85
- return this.currentValue[index];
86
- } catch {
87
- const index = this.currentValue.findIndex((item) => item.tempFilePath === file.tempFilePath);
88
- if (index < 0) {
89
- return;
115
+ upload() {
116
+ return Promise.all(
117
+ this.currentValue.map(async (file) => {
118
+ if (file.uploadStatus !== 'ready') return file;
119
+
120
+ try {
121
+ file.uploadStatus = 'uploading';
122
+ const res = await this.uploadHandler(file);
123
+
124
+ const index = this.currentValue.findIndex((item) => item.tempFilePath === file.tempFilePath);
125
+ if (index < 0) {
126
+ return null;
127
+ }
128
+
129
+ const currentValue = [...this.currentValue];
130
+ currentValue.splice(
131
+ index,
132
+ 1,
133
+ res && res.url
134
+ ? {
135
+ ...file,
136
+ name: res.name,
137
+ url: res.url,
138
+ type: file.type,
139
+ uploadStatus: 'success',
140
+ }
141
+ : {
142
+ ...file,
143
+ uploadStatus: 'error',
144
+ }
145
+ );
146
+ this.currentValue = currentValue;
147
+ return this.currentValue[index];
148
+ } catch {
149
+ const index = this.currentValue.findIndex((item) => item.tempFilePath === file.tempFilePath);
150
+ if (index < 0) {
151
+ return null;
152
+ }
153
+
154
+ const currentValue = [...this.currentValue];
155
+ currentValue.splice(index, 1, { ...file, uploadStatus: 'error' });
156
+ this.currentValue = currentValue;
157
+ return this.currentValue[index];
90
158
  }
91
- const currentValue = [...this.currentValue];
92
- currentValue.splice(index, 1, { ...file, uploadStatus: 'error' });
93
- this.currentValue = currentValue;
94
- }
95
- });
159
+ })
160
+ );
96
161
  },
97
162
  /** 删除文件 */
98
163
  deleteFile(index) {
@@ -5,7 +5,7 @@ export default {
5
5
  /* #ifdef MP */
6
6
  data() {
7
7
  return {
8
- // 使 $style 变为响应式,否则在 wxml 中无法使用
8
+ // 添加 $style 属性,否则在 wxml 中无法使用
9
9
  $style: {},
10
10
  };
11
11
  },
@@ -25,7 +25,7 @@ export default {
25
25
  },
26
26
  /** 合并后的组件类名 */
27
27
  componentClassNames() {
28
- const baseClass = this.$style[this.$options.name.replace(/^App/, '')];
28
+ const baseClass = this.$style?.[this.$options.name.replace(/^App/, '')];
29
29
  return [...(baseClass ? [baseClass] : []), this.componentClass, this.componentKebabClass];
30
30
  },
31
31
  },
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
- "name": "@ecolovo/uniapp-components",
3
- "version": "1.0.3",
4
- "description": "Ecolovo UniApp 组件库",
5
- "keywords": [
6
- "uniapp",
7
- "components"
8
- ],
9
- "author": "Yang",
10
- "license": "ISC",
11
- "dependencies": {
12
- "case-anything": "3.1.2",
13
- "lodash-es": "4.17.21"
14
- }
15
- }
2
+ "name": "@ecolovo/uniapp-components",
3
+ "version": "1.0.5",
4
+ "description": "Ecolovo UniApp 组件库",
5
+ "keywords": [
6
+ "uniapp",
7
+ "components"
8
+ ],
9
+ "author": "Yang",
10
+ "license": "ISC",
11
+ "packageManager": "pnpm@10.10.0",
12
+ "dependencies": {
13
+ "case-anything": "3.1.2",
14
+ "lodash-es": "4.17.21"
15
+ }
16
+ }
@@ -0,0 +1,39 @@
1
+ import { floor } from './floor';
2
+
3
+ const ONE_KB = 1024;
4
+ const ONE_MB = ONE_KB * 1024;
5
+ const ONE_GB = ONE_MB * 1024;
6
+
7
+ /**
8
+ * 格式化文件尺寸
9
+ * @param {number} size 文件大小 B
10
+ * @returns {{
11
+ * size: number;
12
+ * unit: 'B' | 'KB' | 'MB' | 'GB';
13
+ * formattedSize: string;
14
+ * }} 格式化后的大小
15
+ */
16
+ export function fileSize(size) {
17
+ if (size < 0) {
18
+ return { size: -1, unit: 'B', formattedSize: '' };
19
+ }
20
+ const rtnObj = {};
21
+ if (size < 100) {
22
+ rtnObj.size = size;
23
+ rtnObj.unit = 'B';
24
+ } else if (size < ONE_KB) {
25
+ rtnObj.size = floor(size / ONE_KB, 2);
26
+ rtnObj.unit = 'KB';
27
+ } else if (size < ONE_MB) {
28
+ rtnObj.size = floor(size / ONE_KB, 0);
29
+ rtnObj.unit = 'KB';
30
+ } else if (size < ONE_GB) {
31
+ rtnObj.size = floor(size / ONE_MB, 1);
32
+ rtnObj.unit = 'MB';
33
+ } else {
34
+ rtnObj.size = floor(size / ONE_GB, 1);
35
+ rtnObj.unit = 'GB';
36
+ }
37
+ rtnObj.formattedSize = `${rtnObj.size} ${rtnObj.unit}`;
38
+ return rtnObj;
39
+ }