@ecolovo/uniapp-components 1.0.10 → 2.0.1

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 (130) hide show
  1. package/README.md +202 -66
  2. package/components/aspect/aspect.vue +24 -15
  3. package/components/badge/badge.scss +20 -0
  4. package/components/badge/badge.vue +49 -40
  5. package/components/button/button.scss +15 -0
  6. package/components/button/button.vue +121 -177
  7. package/components/checkbox/checkbox.scss +10 -0
  8. package/components/checkbox/checkbox.vue +118 -146
  9. package/components/checkbox-group/checkbox-group.vue +79 -85
  10. package/components/checkbox-group2/checkbox-group2.vue +111 -107
  11. package/components/checkbox-group2/checkbox2.vue +104 -0
  12. package/components/data-list/data-list.scss +9 -0
  13. package/components/data-list/data-list.vue +70 -79
  14. package/components/date-picker/date-picker-view.vue +125 -124
  15. package/components/date-picker/date-picker.vue +145 -146
  16. package/components/dialog/dialog-content.vue +39 -47
  17. package/components/dialog/dialog.scss +11 -0
  18. package/components/dialog/dialog.vue +125 -138
  19. package/components/dropdown/dropdown.scss +11 -0
  20. package/components/dropdown/dropdown.vue +55 -97
  21. package/components/empty/empty.scss +14 -0
  22. package/components/empty/empty.vue +42 -41
  23. package/components/flex/flex.vue +45 -54
  24. package/components/form/form.vue +29 -24
  25. package/components/form-item/form-item.scss +11 -0
  26. package/components/form-item/form-item.vue +43 -40
  27. package/components/icon/icon.vue +70 -85
  28. package/components/image/image.vue +35 -38
  29. package/components/input/input.vue +65 -79
  30. package/components/input/password-input.vue +34 -42
  31. package/components/list-page/list-page.vue +73 -99
  32. package/components/load-more/load-more.scss +12 -0
  33. package/components/load-more/load-more.vue +29 -29
  34. package/components/loading/animations/loading-animation1.vue +8 -8
  35. package/components/loading/animations/loading-animation2.vue +8 -9
  36. package/components/loading/animations/loading-animation3.vue +11 -11
  37. package/components/loading/animations/loading-animation4.vue +8 -8
  38. package/components/loading/loading.vue +19 -20
  39. package/components/message-box/message-box.vue +59 -41
  40. package/components/number-dialog/number-dialog.scss +9 -0
  41. package/components/number-dialog/number-dialog.vue +209 -204
  42. package/components/number-input/number-input.scss +9 -0
  43. package/components/number-input/number-input.vue +233 -232
  44. package/components/numpad/numpad.scss +9 -0
  45. package/components/numpad/numpad.vue +133 -150
  46. package/components/page/page-body.scss +15 -0
  47. package/components/page/page-body.vue +137 -119
  48. package/components/page/page-footer.scss +13 -0
  49. package/components/page/page-footer.vue +31 -33
  50. package/components/page/page-header.scss +17 -0
  51. package/components/page/page-header.vue +89 -94
  52. package/components/page/page.scss +9 -0
  53. package/components/page/page.vue +105 -81
  54. package/components/picker/picker-popup.vue +218 -209
  55. package/components/picker/picker.vue +99 -104
  56. package/components/popover/popover-content.vue +54 -63
  57. package/components/popover/popover-mask.vue +63 -63
  58. package/components/popover/popover.scss +28 -0
  59. package/components/popover/popover.vue +49 -47
  60. package/components/popup/popup-content.vue +49 -53
  61. package/components/popup/popup.scss +11 -0
  62. package/components/popup/popup.vue +117 -132
  63. package/components/price/price.scss +20 -0
  64. package/components/price/price.vue +31 -42
  65. package/components/search/search.scss +12 -0
  66. package/components/search/search.vue +93 -117
  67. package/components/searching/searching.vue +33 -40
  68. package/components/selector/selector.vue +43 -48
  69. package/components/switch/switch.scss +12 -0
  70. package/components/switch/switch.vue +99 -96
  71. package/components/tab/tab.scss +19 -0
  72. package/components/tab/tab.vue +151 -145
  73. package/components/textarea/textarea.vue +104 -101
  74. package/composable/component-name.d.ts +2 -0
  75. package/composable/component-name.js +8 -0
  76. package/composable/component-name.ts +10 -0
  77. package/configs/index.d.ts +25 -10
  78. package/configs/index.js +14 -23
  79. package/configs/index.ts +82 -0
  80. package/easyCom.d.ts +9 -0
  81. package/easyCom.js +1 -2
  82. package/easyCom.ts +31 -0
  83. package/index.d.ts +1 -0
  84. package/index.js +1 -0
  85. package/index.ts +1 -0
  86. package/init.d.ts +4 -7
  87. package/init.js +5 -4
  88. package/init.ts +34 -0
  89. package/package.json +14 -4
  90. package/styles/utils/css-var.scss +12 -15
  91. package/styles/utils/ellipsis.scss +0 -7
  92. package/styles/utils/z-index.scss +1 -1
  93. package/styles/var/component.scss +21 -245
  94. package/utils/date.d.ts +47 -0
  95. package/utils/date.js +23 -43
  96. package/utils/date.ts +153 -0
  97. package/utils/freezeDeep.d.ts +1 -0
  98. package/utils/freezeDeep.js +4 -14
  99. package/utils/freezeDeep.ts +9 -0
  100. package/utils/price.d.ts +24 -0
  101. package/utils/price.js +14 -8
  102. package/utils/price.ts +57 -0
  103. package/utils/round.d.ts +2 -0
  104. package/utils/round.js +2 -14
  105. package/utils/round.ts +11 -0
  106. package/utils/windowInfo.d.ts +24 -0
  107. package/utils/windowInfo.js +8 -36
  108. package/utils/windowInfo.ts +50 -0
  109. package/components/checkbox-group2/checkbox.vue +0 -109
  110. package/components/datetime-picker/datetime-picker-view.vue +0 -209
  111. package/components/datetime-picker/datetime-picker.vue +0 -235
  112. package/components/form/mixin.js +0 -13
  113. package/components/list-page/list-page.js +0 -10
  114. package/components/table/table.vue +0 -112
  115. package/components/time-picker/time-picker-view.vue +0 -127
  116. package/components/time-picker/time-picker.vue +0 -140
  117. package/components/uploader/download.js +0 -81
  118. package/components/uploader/file-uploader.vue +0 -52
  119. package/components/uploader/files.js +0 -225
  120. package/components/uploader/media-uploader.vue +0 -189
  121. package/components/uploader/mixin.js +0 -171
  122. package/components/uploader/uni.js +0 -38
  123. package/components/uploader/upload-button.vue +0 -29
  124. package/components/uploader/uploader copy.vue +0 -547
  125. package/components/uploader/uploader.vue +0 -117
  126. package/mixins/componentName.js +0 -32
  127. package/mixins/listPage.js +0 -1
  128. package/mixins/windowInfo.js +0 -1
  129. package/utils/fileSize.js +0 -39
  130. package/utils/floor.js +0 -20
@@ -1,52 +0,0 @@
1
- <template>
2
- <view :class="componentClassNames">
3
- <view>
4
- <button-cpn>上传</button-cpn>
5
- </view>
6
- <view>
7
- <view v-for="(file, idx) in currentValue" :key="idx" :class="$style['Uploader--file-item']">
8
- {{ file.name || file.path }}
9
- </view>
10
- </view>
11
- </view>
12
- </template>
13
-
14
- <script>
15
- import componentName from '../../mixins/componentName';
16
- import ButtonCpn from '../button/button.vue';
17
- import UploadMixin from './mixin';
18
-
19
- export default {
20
- name: 'Uploader',
21
- options: {
22
- virtualHost: true,
23
- styleIsolation: 'shared',
24
- },
25
- components: { ButtonCpn },
26
- mixins: [componentName, UploadMixin],
27
- props: {
28
- /** 文件列表 */
29
- value: {
30
- type: Array,
31
- default: () => [],
32
- },
33
- },
34
- methods: {},
35
- };
36
- </script>
37
-
38
- <style lang="scss" module>
39
- .Uploader {
40
- width: 100%;
41
- }
42
- .Uploader--file-item {
43
- &:first-child {
44
- margin-top: 20rpx;
45
- }
46
- padding: 15rpx 0;
47
- font-size: 28rpx;
48
- white-space: nowrap;
49
- overflow: hidden;
50
- text-overflow: ellipsis;
51
- }
52
- </style>
@@ -1,225 +0,0 @@
1
- export function randomInt(max = 100, min = 0, padStart = false) {
2
- const v = Math.floor(Math.random() * max + min);
3
- if (!padStart) return v;
4
- const n = (max - 1).toString().length;
5
- return v.toString().padStart(n, '0');
6
- }
7
-
8
- const FILE_TYPE_IMAGE = 'image';
9
- const FILE_TYPE_VIDEO = 'video';
10
- const FILE_TYPE_FILE = 'file';
11
-
12
- /**
13
- * 文件
14
- * @typedef {object} ChosenFile
15
- * @property {string} name 文件名
16
- * @property {number} size 文件大小
17
- * @property {string} path 路径
18
- * @property {string} type 类型
19
- */
20
-
21
- /**
22
- * 文件类型字符串是否有效
23
- * @param {string} type 文件类型字符串 'image' 、 'video' 、 'file' 的组合或 'all'
24
- * @returns {boolean} 是否有效
25
- */
26
- function typeStringValidator(type) {
27
- if (type === 'all') return true;
28
- const arr = typeStringToArr(type);
29
- if (arr.length === 0) return false;
30
- return arr.every((item) => [FILE_TYPE_IMAGE, FILE_TYPE_VIDEO, FILE_TYPE_FILE].includes(item));
31
- }
32
-
33
- /**
34
- * 将文件类型字符串转换为文件类型数组
35
- * @param {string} type 文件类型字符串 'image' 、 'video' 、 'file' 的组合或 'all'
36
- * @param {string} divider 分隔符
37
- * @returns {string[]} 文件类型数组
38
- */
39
- function typeStringToArr(type, divider = ',') {
40
- if (Array.isArray(type)) return type;
41
- if (type === 'all') return [FILE_TYPE_IMAGE, FILE_TYPE_VIDEO, FILE_TYPE_FILE];
42
- return [...new Set(type.split(divider))];
43
- }
44
-
45
- /**
46
- * 将文件类型数组转换为 {@link uni.chooseMessageFile} 的 `type` 参数
47
- * @param {('image' | 'video' | 'file')[]} arr 文件类型,可选 'image' 、 'video' 、 'file' 组成的数组
48
- * @returns {'image' | 'video' | 'file' | 'all'}
49
- */
50
- function typeArrToChooseMessageFileType(arr) {
51
- if (!arr || !Array.isArray(arr)) throw 'arr is invalid';
52
-
53
- const image = arr.includes(FILE_TYPE_IMAGE);
54
- const video = arr.includes(FILE_TYPE_VIDEO);
55
- const file = arr.includes(FILE_TYPE_FILE);
56
- if (image && video && file) return 'all';
57
-
58
- if (file) return FILE_TYPE_FILE;
59
- if (image) return FILE_TYPE_IMAGE;
60
- if (video) return FILE_TYPE_VIDEO;
61
- throw 'arr is invalid';
62
- }
63
-
64
- /**
65
- * 将文件类型数组转换为 {@link uni.chooseMessageFile} 的 `type` 参数
66
- * @param {('image' | 'video' | 'file')[] | undefined} arr 文件类型,可选 'image' 、 'video' 组成的数组
67
- * @returns {('image' | 'video')[]}
68
- */
69
- function typeArrToChooseMediaType(arr) {
70
- if (!arr || !Array.isArray(arr)) throw 'arr is invalid';
71
-
72
- const image = arr.includes(FILE_TYPE_IMAGE);
73
- const video = arr.includes(FILE_TYPE_VIDEO);
74
- if (image && video) return [FILE_TYPE_IMAGE, FILE_TYPE_VIDEO];
75
-
76
- if (image) return [FILE_TYPE_IMAGE];
77
- if (video) return [FILE_TYPE_VIDEO];
78
- throw 'arr is invalid';
79
- }
80
-
81
- function getFileExt(filename) {
82
- return filename.split('.').pop();
83
- }
84
-
85
- function isImage(filename) {
86
- return ['jpg', 'jpeg', 'png'].includes(getFileExt(filename));
87
- }
88
-
89
- function isVideo(filename) {
90
- return ['mp4', 'mov', 'mkv', 'wmv', 'avi', 'flv'].includes(getFileExt(filename));
91
- }
92
-
93
- /**
94
- * @typedef {object} chooseFileResult
95
- * @property {ChosenFile[]} files 有效的文件列表
96
- * @property {ChosenFile[]} invalidFiles 无效的文件列表
97
- */
98
- /**
99
- * 过滤文件
100
- * @param {ChosenFile[]} files 选择的文件列表
101
- * @param {ChosenFile[]} extensionList 限制的文件类型
102
- * @param {string} key 读取文件名的 key
103
- * @returns {chooseFileResult} 过滤后的结果
104
- */
105
- function filterFiles(files, extensionList, key = 'name') {
106
- const invalidFiles = [];
107
- if (extensionList && extensionList.length > 0) {
108
- files = files.filter((item) => {
109
- const ext = getFileExt(item[key]);
110
- if (extensionList.includes(ext)) {
111
- return item;
112
- }
113
- invalidFiles.push(item);
114
- });
115
- }
116
- return { files, invalidFiles };
117
- }
118
-
119
- /**
120
- * @typedef {object} chooseFileParams
121
- * @property {number} max 最大上传数量,不能超过 9
122
- * @property {('image' | 'video' | 'file')[] | undefined} type 文件类型,可选 'image' 、 'video' 、 'file' 组成的数组
123
- * @property {string[]} selectExtension 选择文件时限制文件类型,不带 ".",仅 type 包含 file 时有效
124
- * @property {string[]} allowedExtension 选中文件后文件类型限制,不带 "."
125
- * @property {boolean} fromChat 是否从聊天文件中选择图片和视频
126
- */
127
- /** 选择文件
128
- * @param {chooseFileParams} param0
129
- * @return {Promise<chooseFileResult>} 选择的文件列表
130
- */
131
- function chooseFile({ max = 1, type, selectExtension, allowedExtension, fromChat = false } = {}) {
132
- /* #ifdef MP-WEIXIN */
133
-
134
- if (type.includes(FILE_TYPE_FILE) || fromChat) {
135
- type = typeArrToChooseMessageFileType(type);
136
-
137
- return new Promise((resolve, reject) => {
138
- uni.chooseMessageFile({
139
- count: Math.max(1, Math.min(max, 100)),
140
- type: type,
141
- extension: selectExtension,
142
- success(res) {
143
- resolve(filterFiles(res.tempFiles, allowedExtension));
144
- },
145
- fail(err) {
146
- reject(err);
147
- },
148
- });
149
- });
150
- }
151
- /* #endif */
152
- type = typeArrToChooseMediaType(type);
153
-
154
- return new Promise((resolve, reject) => {
155
- uni.chooseMedia({
156
- count: Math.max(1, Math.min(max, 20)),
157
- mediaType: type,
158
- sizeType: ['original', 'compressed'],
159
- sourceType: ['album', 'camera'],
160
- success(res) {
161
- resolve(
162
- filterFiles(
163
- res.tempFiles.map((item) => ({
164
- name:
165
- (item.fileType === FILE_TYPE_IMAGE ? '图片' : '视频') +
166
- '_' +
167
- Date.now() +
168
- randomInt(10000, 0, true) +
169
- '.' +
170
- getFileExt(item.tempFilePath),
171
- size: item.size,
172
- path: item.tempFilePath,
173
- type: item.fileType,
174
- })),
175
- allowedExtension
176
- )
177
- );
178
- },
179
- fail(err) {
180
- reject(err);
181
- },
182
- });
183
- });
184
- }
185
-
186
- /**
187
- * 保存文件
188
- * @param {string} path 文件临时路径
189
- * @returns 保存后的路径
190
- */
191
- function saveFile(path, name) {
192
- return new Promise((resolve, reject) => {
193
- /* #ifdef MP-WEIXIN */
194
- uni.getFileSystemManager().saveFile({
195
- tempFilePath: path,
196
- filePath: `${wx.env.USER_DATA_PATH}/${name}`,
197
- success: function (res) {
198
- resolve(res.savedFilePath);
199
- },
200
- fail: reject,
201
- });
202
- /* #endif */
203
- /* #ifndef MP-WEIXIN */
204
- uni.saveFile({
205
- tempFilePath: path,
206
- success: function (res) {
207
- resolve(res.savedFilePath);
208
- },
209
- fail: reject,
210
- });
211
- /* #endif */
212
- });
213
- }
214
-
215
- export {
216
- FILE_TYPE_IMAGE,
217
- FILE_TYPE_VIDEO,
218
- FILE_TYPE_FILE,
219
- typeStringToArr,
220
- typeStringValidator,
221
- chooseFile,
222
- isImage,
223
- isVideo,
224
- saveFile,
225
- };
@@ -1,189 +0,0 @@
1
- <template>
2
- <view :class="componentClassNames" :style="{ '--columns': columns }">
3
- <!-- 已上传文件列表 -->
4
- <aspect-cpn v-for="(item, idx) in fileList" :key="idx" :class="$style['Uploader--item']" :style="styleWidth">
5
- <!-- TODO: 视频文件支持 -->
6
- <!-- <video
7
- v-if="item.fileType === 'video'"
8
- :muted="true"
9
- :src="item.fileUrl"
10
- :controls="false"
11
- :show-progress="false"
12
- :show-fullscreen-btn="false"
13
- :show-play-btn="false"
14
- :show-center-play-btn="false"
15
- :show-loading="false"
16
- :enable-progress-gesture="false"
17
- object-fit="fill"
18
- :class="[$style['Uploader--image'], $style['Uploader--video']]"
19
- ></video> -->
20
- <image-cpn
21
- :src="item.tempFilePath || item.url"
22
- :preview-list="previewList"
23
- :class="$style['Uploader--image']"
24
- :style="styleSize"
25
- mode="aspectFill"
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>
34
- <view
35
- v-if="!isReadonly && item.uploadStatus !== 'uploading'"
36
- :class="$style['Uploader--icon-delete']"
37
- @click.stop="deleteFile(idx)"
38
- >
39
- </view>
40
- </aspect-cpn>
41
- <!-- 上传按钮 -->
42
- <aspect-cpn
43
- v-if="!isReadonly && restCount > 0"
44
- :class="$style['Uploader--item']"
45
- :style="styleWidth"
46
- @click="selectFile"
47
- >
48
- <slot>
49
- <button-cpn plain :class="$style['Uploader--button']">
50
- <view style="line-height: 1.3em">
51
- <view>上传</view>
52
- <view>图片</view>
53
- </view>
54
- </button-cpn>
55
- </slot>
56
- </aspect-cpn>
57
- </view>
58
- </template>
59
-
60
- <script>
61
- import componentName from '../../mixins/componentName';
62
- import AspectCpn from '../aspect/aspect.vue';
63
- import ButtonCpn from '../button/button.vue';
64
- import ImageCpn from '../image/image.vue';
65
- import LoadingCpn from '../loading/animations/loading-animation4.vue';
66
- import UploadMixin from './mixin';
67
-
68
- export default {
69
- name: 'Uploader',
70
- options: {
71
- virtualHost: true,
72
- styleIsolation: 'shared',
73
- },
74
- components: { AspectCpn, ButtonCpn, ImageCpn, LoadingCpn },
75
- mixins: [componentName, UploadMixin],
76
- props: {
77
- /** 媒体类型,可选值:'image' | 'video' | 'image,video' */
78
- type: {
79
- type: String,
80
- default: 'image',
81
- },
82
- /** 媒体来源,可选值:'album' | 'camera' | 'album,camera' */
83
- source: {
84
- type: String,
85
- default: 'album',
86
- },
87
- /** 列数 */
88
- columns: {
89
- type: [String, Number],
90
- default: 4,
91
- },
92
- /** 显示宽度 */
93
- width: {
94
- type: [String, Number],
95
- default: '',
96
- },
97
- },
98
- computed: {
99
- styleWidth() {
100
- if (!this.width) return '';
101
- const width = typeof this.width === 'number' ? `${this.width}rpx` : this.width;
102
- return `width: ${width};`;
103
- },
104
- /** 预览列表 */
105
- previewList() {
106
- return this.fileList.map((item) => item.tempFilePath || item.url);
107
- },
108
- },
109
- methods: {
110
- selectFile() {
111
- uni.chooseMedia({
112
- count: this.restCount,
113
- mediaType: this.type.split(','),
114
- sourceType: this.source.split(','),
115
- success: (res) => {
116
- const files = this.validateFiles(res.tempFiles);
117
- if (this.autoUpload && files) {
118
- this.upload();
119
- }
120
- },
121
- });
122
- },
123
- },
124
- };
125
- </script>
126
-
127
- <style lang="scss" module>
128
- .Uploader {
129
- width: 100%;
130
- display: flex;
131
- flex-wrap: wrap;
132
- gap: 12rpx;
133
- }
134
-
135
- .Uploader--item {
136
- position: relative;
137
- width: calc(100% / var(--columns) - 12rpx * (var(--columns) - 1) / var(--columns));
138
- }
139
- .Uploader--image {
140
- border-radius: 16rpx;
141
- }
142
- .Uploader--upload-loading {
143
- pointer-events: none;
144
- position: absolute;
145
- top: 50%;
146
- left: 50%;
147
- font-size: 2em;
148
- color: #fff;
149
- transform: translate(-50%, -50%);
150
- }
151
- .Uploader--upload-error {
152
- pointer-events: none;
153
- position: absolute;
154
- top: 50%;
155
- left: 50%;
156
- width: 2em;
157
- height: 2em;
158
- box-sizing: content-box;
159
- transform: translate(-50%, -50%);
160
- padding: 1.25em;
161
- border-radius: 50%;
162
- background-color: #00000080;
163
- color: #fff;
164
- font-size: 22rpx;
165
- display: flex;
166
- justify-content: center;
167
- align-items: center;
168
- }
169
- .Uploader--button {
170
- height: 100%;
171
- border-color: #ccc;
172
- border-radius: 16rpx;
173
- }
174
-
175
- .Uploader--icon-delete {
176
- position: absolute;
177
- top: 0;
178
- right: 0;
179
- z-index: 1;
180
- transform-origin: 75%, 25%;
181
- width: 36rpx;
182
- height: 36rpx;
183
- background: no-repeat top / 100% 45% linear-gradient(90deg, red 45%, transparent 45%, transparent 55%, red 55%),
184
- no-repeat bottom / 100% 45% linear-gradient(90deg, red 45%, transparent 45%, transparent 55%, red 55%),
185
- radial-gradient(transparent 50%, red 50%, red 100%);
186
- transform: translate(25%, -25%) rotate(45deg);
187
- border-radius: 50%;
188
- }
189
- </style>
@@ -1,171 +0,0 @@
1
- import { fileSize } from '../../utils/fileSize';
2
- import { isFormReadonly } from '../form/mixin';
3
-
4
- export default {
5
- mixins: [isFormReadonly],
6
- props: {
7
- /** 绑定值,文件列表 */
8
- value: {
9
- type: Array,
10
- default: () => [],
11
- },
12
- /** 是否只读 */
13
- readonly: {
14
- type: Boolean,
15
- default: false,
16
- },
17
- /** 最大上传文件数 */
18
- max: {
19
- type: Number,
20
- default: 9,
21
- },
22
- /** 最大上传文件大小 */
23
- maxSize: {
24
- type: Number,
25
- default: 1024 * 1024 * 10, // 10MB
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
- */
38
- uploadHandler: {
39
- type: Function,
40
- default: null,
41
- },
42
- },
43
- data() {
44
- return {
45
- innerValue: this.value,
46
- };
47
- },
48
- computed: {
49
- isReadonly() {
50
- return this.readonly || this.isFormReadonly;
51
- },
52
- /** 剩余可上传文件数 */
53
- restCount() {
54
- if (this.max <= 0) return 9;
55
- return this.max - this.currentValue.length;
56
- },
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() {
68
- return this.currentValue.map((item) =>
69
- typeof item === 'string' ? { url: item, uploadStatus: 'success' } : { uploadStatus: 'success', ...item }
70
- );
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
- },
85
- },
86
- methods: {
87
- /** 校验待上传文件 */
88
- validateFiles(files) {
89
- if (files.length === 0) return;
90
-
91
- // 过滤大文件
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
- });
99
- }
100
- if (selectedFiles.length === 0) return;
101
-
102
- // 校验数量
103
- if (selectedFiles.length > this.restCount) {
104
- uni.showToast({ icon: 'none', title: `最多只能上传${this.max}个文件`, duration: 2000 });
105
- }
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;
113
- },
114
- /** 上传文件 */
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];
158
- }
159
- })
160
- );
161
- },
162
- /** 删除文件 */
163
- deleteFile(index) {
164
- if (index >= 0 && index < this.currentValue.length) {
165
- const currentValue = [...this.currentValue];
166
- currentValue.splice(index, 1);
167
- this.currentValue = currentValue;
168
- }
169
- },
170
- },
171
- };
@@ -1,38 +0,0 @@
1
- import { download } from './download';
2
-
3
- function canOpenDocument(filename) {
4
- return ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'].includes(filename.split('.').pop());
5
- }
6
-
7
- /**
8
- * 新开页面打开文档,支持格式:doc, docx, xls, xlsx, ppt, pptx, pdf
9
- * @param {string} url 文件 url
10
- * @param {string} name 文件名
11
- */
12
- export function openDocument(url, name) {
13
- return download(url, name)
14
- .catch(() => {
15
- return Promise.reject('文件下载失败');
16
- })
17
- .then((res) => {
18
- var path = res.filePath;
19
- if (!canOpenDocument(path)) {
20
- return Promise.reject('该文件类型不支持预览');
21
- }
22
- return new Promise((resolve, reject) => {
23
- uni.openDocument({
24
- filePath: path,
25
- showMenu: true,
26
- success: resolve,
27
- fail: reject,
28
- });
29
- });
30
- });
31
- }
32
-
33
- /** 复制文本 */
34
- export function copy(text) {
35
- uni.setClipboardData({
36
- data: text,
37
- });
38
- }
@@ -1,29 +0,0 @@
1
- <template>
2
- <button-cpn plain :class="$style['Uploader--card-uploader']" @click="$emit('select')">
3
- <slot>上传</slot>
4
- </button-cpn>
5
- </template>
6
-
7
- <script>
8
- import componentName from '../../mixins/componentName';
9
- import ButtonCpn from '../button/button.vue';
10
-
11
- export default {
12
- name: 'UploadButton',
13
- options: {
14
- virtualHost: true,
15
- styleIsolation: 'shared',
16
- },
17
- components: { ButtonCpn },
18
- mixins: [componentName],
19
- props: {
20
- /** 文件列表 */
21
- value: {
22
- type: Array,
23
- default: () => [],
24
- },
25
- },
26
- computed: {},
27
- methods: {},
28
- };
29
- </script>