im-ui-mobile 0.1.0 → 0.1.2
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.
- package/components/im-avatar/im-avatar.vue +7 -7
- package/components/im-badge/im-badge.vue +326 -0
- package/components/im-button/im-button.vue +71 -34
- package/components/im-card/im-card.vue +563 -0
- package/components/im-chat-item/im-chat-item.vue +5 -4
- package/components/im-col/im-col.vue +191 -0
- package/components/im-dialog/im-dialog.vue +543 -0
- package/components/im-double-tap-view/im-double-tap-view.vue +93 -0
- package/components/im-emoji-picker/im-emoji-picker.vue +1143 -0
- package/components/im-friend-item/im-friend-item.vue +1 -1
- package/components/im-group-item/im-group-item.vue +1 -1
- package/components/im-group-member-selector/im-group-member-selector.vue +5 -5
- package/components/im-group-rtc-join/im-group-rtc-join.vue +8 -8
- package/components/im-icon/im-icon.vue +593 -0
- package/components/im-image-upload/im-image-upload.vue +0 -2
- package/components/im-link/im-link.vue +628 -0
- package/components/im-loading/im-loading.vue +13 -4
- package/components/im-mention-picker/im-mention-picker.vue +8 -7
- package/components/im-message-action/im-message-action.vue +678 -0
- package/components/im-message-item/im-message-item.vue +28 -26
- package/components/im-message-list/im-message-list.vue +1108 -0
- package/components/im-modal/im-modal.vue +373 -0
- package/components/im-nav-bar/im-nav-bar.vue +689 -75
- package/components/im-parse/im-parse.vue +1054 -0
- package/components/im-popup/im-popup.vue +467 -0
- package/components/im-read-receipt/im-read-receipt.vue +10 -10
- package/components/im-row/im-row.vue +189 -0
- package/components/im-search/im-search.vue +762 -0
- package/components/im-sku/im-sku.vue +720 -0
- package/components/im-sku/utils/helper.ts +182 -0
- package/components/im-stepper/im-stepper.vue +585 -0
- package/components/im-stepper/utils/helper.ts +167 -0
- package/components/im-tabs/im-tabs.vue +1022 -0
- package/components/im-tabs/tabs-navigation.vue +489 -0
- package/components/im-tabs/utils/helper.ts +181 -0
- package/components/im-tabs-tab-pane/im-tabs-tab-pane.vue +145 -0
- package/components/im-upload/im-upload.vue +1236 -0
- package/components/im-voice-input/im-voice-input.vue +1 -1
- package/index.js +3 -5
- package/index.scss +19 -0
- package/libs/emoji-data.ts +229 -0
- package/libs/index.ts +16 -16
- package/package.json +1 -2
- package/styles/button.scss +33 -33
- package/theme.scss +2 -2
- package/types/components/badge.d.ts +42 -0
- package/types/components/button.d.ts +2 -1
- package/types/components/card.d.ts +122 -0
- package/types/components/col.d.ts +37 -0
- package/types/components/dialog.d.ts +125 -0
- package/types/components/double-tap-view.d.ts +31 -0
- package/types/components/emoji-picker.d.ts +121 -0
- package/types/components/group-rtc-join.d.ts +1 -1
- package/types/components/icon.d.ts +77 -0
- package/types/components/link.d.ts +55 -0
- package/types/components/loading.d.ts +1 -0
- package/types/components/message-action.d.ts +96 -0
- package/types/components/message-item.d.ts +2 -2
- package/types/components/message-list.d.ts +136 -0
- package/types/components/modal.d.ts +106 -0
- package/types/components/nav-bar.d.ts +125 -0
- package/types/components/parse.d.ts +90 -0
- package/types/components/popup.d.ts +58 -0
- package/types/components/row.d.ts +31 -0
- package/types/components/search.d.ts +54 -0
- package/types/components/sku.d.ts +195 -0
- package/types/components/stepper.d.ts +99 -0
- package/types/components/tabs-tab-pane.d.ts +27 -0
- package/types/components/tabs.d.ts +117 -0
- package/types/components/upload.d.ts +137 -0
- package/types/components.d.ts +19 -1
- package/types/index.d.ts +38 -1
- package/types/libs/index.d.ts +10 -10
- package/types/utils/base64.d.ts +5 -0
- package/types/utils/dom.d.ts +3 -0
- package/types/utils/enums.d.ts +4 -5
- package/types/utils/validator.d.ts +74 -0
- package/utils/base64.js +18 -0
- package/utils/dom.js +353 -1
- package/utils/enums.js +4 -5
- package/utils/validator.js +230 -0
- package/components/im-file-upload/im-file-upload.vue +0 -309
- package/plugins/uview-plus.js +0 -29
- package/types/components/arrow-bar.d.ts +0 -14
- package/types/components/file-upload.d.ts +0 -58
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 校验是否为纯数字
|
|
3
|
+
* @param {string|number} value - 待校验的值
|
|
4
|
+
* @returns {boolean}
|
|
5
|
+
*/
|
|
6
|
+
function isNumber(value) {
|
|
7
|
+
return /^\d+$/.test(String(value));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 校验整数(可包含正负号)
|
|
12
|
+
* @param {string|number} value - 待校验的值
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isInteger(value) {
|
|
16
|
+
return /^-?\d+$/.test(String(value));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 校验小数(可指定小数位数)
|
|
21
|
+
* @param {string|number} value - 待校验的值
|
|
22
|
+
* @param {number} decimalPlaces - 小数位数,默认为2
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
function isDecimal(value, decimalPlaces = 2) {
|
|
26
|
+
const regex = new RegExp(`^-?\\d+(\\.\\d{1,${decimalPlaces}})?$`);
|
|
27
|
+
return regex.test(String(value));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 校验正整数
|
|
32
|
+
* @param {string|number} value - 待校验的值
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
function isPositiveInteger(value) {
|
|
36
|
+
return /^[1-9]\d*$/.test(String(value));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 中国大陆手机号校验(11位,以1开头)
|
|
41
|
+
* @param {string} phone - 手机号码
|
|
42
|
+
* @returns {boolean}
|
|
43
|
+
*/
|
|
44
|
+
function isMobilePhone(phone) {
|
|
45
|
+
return /^1[3-9]\d{9}$/.test(phone);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 国际通用电话校验(简化版)
|
|
50
|
+
* @param {string} phone - 电话号码
|
|
51
|
+
* @returns {boolean}
|
|
52
|
+
*/
|
|
53
|
+
function isInternationalPhone(phone) {
|
|
54
|
+
return /^\+?[\d\s\-\(\)]{10,}$/.test(phone);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 固话号码校验(带区号)
|
|
59
|
+
* @param {string} phone - 固定电话
|
|
60
|
+
* @returns {boolean}
|
|
61
|
+
*/
|
|
62
|
+
function isLandlinePhone(phone) {
|
|
63
|
+
return /^0\d{2,3}-\d{7,8}$/.test(phone);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 邮箱格式校验
|
|
68
|
+
* @param {string} email - 邮箱地址
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
*/
|
|
71
|
+
function isEmail(email) {
|
|
72
|
+
const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
|
73
|
+
return regex.test(email);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 严格邮箱校验(包含更多规则)
|
|
78
|
+
* @param {string} email - 邮箱地址
|
|
79
|
+
* @returns {boolean}
|
|
80
|
+
*/
|
|
81
|
+
function isEmailStrict(email) {
|
|
82
|
+
const regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
83
|
+
return regex.test(email);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 中国大陆身份证校验(15位或18位)
|
|
88
|
+
* @param {string} idCard - 身份证号码
|
|
89
|
+
* @returns {boolean}
|
|
90
|
+
*/
|
|
91
|
+
function isChineseIDCard(idCard) {
|
|
92
|
+
// 基础格式校验
|
|
93
|
+
const regex = /(^\d{15}$)|(^\d{17}(\d|X|x)$)/;
|
|
94
|
+
if (!regex.test(idCard)) return false;
|
|
95
|
+
|
|
96
|
+
// 18位身份证校验码验证
|
|
97
|
+
if (idCard.length === 18) {
|
|
98
|
+
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
|
|
99
|
+
const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
|
|
100
|
+
|
|
101
|
+
let sum = 0;
|
|
102
|
+
for (let i = 0; i < 17; i++) {
|
|
103
|
+
sum += parseInt(idCard[i]) * weights[i];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const checkCode = checkCodes[sum % 11];
|
|
107
|
+
return checkCode === idCard[17].toUpperCase();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return true; // 15位身份证暂不做严格校验
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* URL校验
|
|
115
|
+
* @param {string} url - URL地址
|
|
116
|
+
* @returns {boolean}
|
|
117
|
+
*/
|
|
118
|
+
function isURL(url) {
|
|
119
|
+
try {
|
|
120
|
+
new URL(url);
|
|
121
|
+
return true;
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 密码强度校验
|
|
129
|
+
* @param {string} password - 密码
|
|
130
|
+
* @param {Object} options - 配置选项
|
|
131
|
+
* @returns {Object} {isValid: boolean, strength: string}
|
|
132
|
+
*/
|
|
133
|
+
function validatePassword(password, options = {}) {
|
|
134
|
+
const {
|
|
135
|
+
minLength = 8,
|
|
136
|
+
requireUppercase = true,
|
|
137
|
+
requireLowercase = true,
|
|
138
|
+
requireNumbers = true,
|
|
139
|
+
requireSpecialChars = true
|
|
140
|
+
} = options;
|
|
141
|
+
|
|
142
|
+
const result = {
|
|
143
|
+
isValid: true,
|
|
144
|
+
strength: '弱',
|
|
145
|
+
messages: []
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// 长度检查
|
|
149
|
+
if (password.length < minLength) {
|
|
150
|
+
result.isValid = false;
|
|
151
|
+
result.messages.push(`密码长度至少${minLength}位`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 大写字母检查
|
|
155
|
+
if (requireUppercase && !/[A-Z]/.test(password)) {
|
|
156
|
+
result.isValid = false;
|
|
157
|
+
result.messages.push('必须包含大写字母');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 小写字母检查
|
|
161
|
+
if (requireLowercase && !/[a-z]/.test(password)) {
|
|
162
|
+
result.isValid = false;
|
|
163
|
+
result.messages.push('必须包含小写字母');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 数字检查
|
|
167
|
+
if (requireNumbers && !/\d/.test(password)) {
|
|
168
|
+
result.isValid = false;
|
|
169
|
+
result.messages.push('必须包含数字');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 特殊字符检查
|
|
173
|
+
if (requireSpecialChars && !/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
|
|
174
|
+
result.isValid = false;
|
|
175
|
+
result.messages.push('必须包含特殊字符');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 评估密码强度
|
|
179
|
+
if (result.isValid) {
|
|
180
|
+
let score = 0;
|
|
181
|
+
if (password.length >= 12) score++;
|
|
182
|
+
if (/[A-Z]/.test(password)) score++;
|
|
183
|
+
if (/[a-z]/.test(password)) score++;
|
|
184
|
+
if (/\d/.test(password)) score++;
|
|
185
|
+
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) score++;
|
|
186
|
+
|
|
187
|
+
if (score >= 4) result.strength = '强';
|
|
188
|
+
else if (score >= 3) result.strength = '中';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 中文姓名校验(2-4个汉字)
|
|
196
|
+
* @param {string} name - 姓名
|
|
197
|
+
* @returns {boolean}
|
|
198
|
+
*/
|
|
199
|
+
function isChineseName(name) {
|
|
200
|
+
return /^[\u4e00-\u9fa5]{2,4}$/.test(name);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 车牌号校验(中国大陆)
|
|
205
|
+
* @param {string} plate - 车牌号
|
|
206
|
+
* @returns {boolean}
|
|
207
|
+
*/
|
|
208
|
+
function isLicensePlate(plate) {
|
|
209
|
+
// 新能源车牌和普通车牌
|
|
210
|
+
const regex = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$/;
|
|
211
|
+
return regex.test(plate);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
export default {
|
|
216
|
+
isNumber,
|
|
217
|
+
isInteger,
|
|
218
|
+
isDecimal,
|
|
219
|
+
isPositiveInteger,
|
|
220
|
+
isMobilePhone,
|
|
221
|
+
isInternationalPhone,
|
|
222
|
+
isLandlinePhone,
|
|
223
|
+
isEmail,
|
|
224
|
+
isEmailStrict,
|
|
225
|
+
isChineseIDCard,
|
|
226
|
+
isURL,
|
|
227
|
+
validatePassword,
|
|
228
|
+
isChineseName,
|
|
229
|
+
isLicensePlate
|
|
230
|
+
}
|
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<view v-if="visible" class="file-upload">
|
|
3
|
-
<u-upload ref="uploadRef" :max-count="props.maxCount" :max-size="props.maxSize" :size-type="props.sizeType"
|
|
4
|
-
:source-type="props.sourceType" :deletable="props.deletable" :preview-full-image="props.previewFullImage"
|
|
5
|
-
:multiple="props.multiple" :disabled="props.disabled" :custom-btn="props.customBtn"
|
|
6
|
-
:show-progress="props.showProgress" :upload-text="props.uploadText" :width="props.width"
|
|
7
|
-
:height="props.height" @after-read="onAfterRead" @delete="onDelete" @oversize="onOversize">
|
|
8
|
-
<!-- 自定义上传按钮 -->
|
|
9
|
-
<slot></slot>
|
|
10
|
-
</u-upload>
|
|
11
|
-
</view>
|
|
12
|
-
</template>
|
|
13
|
-
|
|
14
|
-
<script setup lang="ts">
|
|
15
|
-
import { ref, reactive, watch } from 'vue'
|
|
16
|
-
|
|
17
|
-
// 定义类型
|
|
18
|
-
interface UploadFile {
|
|
19
|
-
url: string
|
|
20
|
-
name?: string
|
|
21
|
-
size?: number
|
|
22
|
-
type?: string
|
|
23
|
-
progress?: number
|
|
24
|
-
status?: 'uploading' | 'success' | 'error'
|
|
25
|
-
response?: any
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface AfterReadFile {
|
|
29
|
-
name: string
|
|
30
|
-
size: number
|
|
31
|
-
thumb: string
|
|
32
|
-
type: string
|
|
33
|
-
url: string
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface UploadProps {
|
|
37
|
-
// 文件列表
|
|
38
|
-
modelValue?: UploadFile[]
|
|
39
|
-
// 最大上传数量
|
|
40
|
-
maxCount?: number
|
|
41
|
-
// 单个文件大小限制(字节)
|
|
42
|
-
maxSize?: number
|
|
43
|
-
// 文件大小选择 ['original', 'compressed']
|
|
44
|
-
sizeType?: string[]
|
|
45
|
-
// 文件来源 ['album', 'camera']
|
|
46
|
-
sourceType?: string[]
|
|
47
|
-
// 是否显示删除按钮
|
|
48
|
-
deletable?: boolean
|
|
49
|
-
// 是否预览完整图片
|
|
50
|
-
previewFullImage?: boolean
|
|
51
|
-
// 是否多选
|
|
52
|
-
multiple?: boolean
|
|
53
|
-
// 是否禁用
|
|
54
|
-
disabled?: boolean
|
|
55
|
-
// 是否使用自定义按钮
|
|
56
|
-
customBtn?: boolean
|
|
57
|
-
// 是否显示进度条
|
|
58
|
-
showProgress?: boolean
|
|
59
|
-
// 上传文字提示
|
|
60
|
-
uploadText?: string
|
|
61
|
-
// 图片宽度
|
|
62
|
-
width?: number | string
|
|
63
|
-
// 图片高度
|
|
64
|
-
height?: number | string
|
|
65
|
-
// 上传地址
|
|
66
|
-
action?: string
|
|
67
|
-
// 上传字段名
|
|
68
|
-
name?: string
|
|
69
|
-
// 请求头
|
|
70
|
-
headers?: Record<string, string>
|
|
71
|
-
// 附加数据
|
|
72
|
-
data?: Record<string, any>
|
|
73
|
-
// 是否自动上传
|
|
74
|
-
autoUpload?: boolean,
|
|
75
|
-
// 令牌
|
|
76
|
-
token?: string
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
interface UploadEmits {
|
|
80
|
-
(e: 'update:modelValue', files: UploadFile[]): void
|
|
81
|
-
(e: 'change', files: UploadFile[]): void
|
|
82
|
-
(e: 'before', file: UploadFile): void
|
|
83
|
-
(e: 'success', file: UploadFile, response: any): void
|
|
84
|
-
(e: 'fail', file: UploadFile, error: any): void
|
|
85
|
-
(e: 'progress', file: UploadFile, progress: number): void
|
|
86
|
-
(e: 'delete', file: UploadFile, index: number): void
|
|
87
|
-
(e: 'oversize', file: UploadFile): void
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Props 和 Emits
|
|
91
|
-
const props = withDefaults(defineProps<UploadProps>(), {
|
|
92
|
-
modelValue: () => [],
|
|
93
|
-
maxCount: 9,
|
|
94
|
-
maxSize: 10 * 1024 * 1024, // 10MB
|
|
95
|
-
sizeType: () => ['original', 'compressed'],
|
|
96
|
-
sourceType: () => ['album', 'camera'],
|
|
97
|
-
deletable: true,
|
|
98
|
-
previewFullImage: true,
|
|
99
|
-
multiple: true,
|
|
100
|
-
disabled: false,
|
|
101
|
-
customBtn: false,
|
|
102
|
-
showProgress: true,
|
|
103
|
-
uploadText: '',
|
|
104
|
-
width: 50,
|
|
105
|
-
height: 50,
|
|
106
|
-
action: '', // https://example.com/file/upload
|
|
107
|
-
name: 'file',
|
|
108
|
-
headers: () => ({
|
|
109
|
-
'Authorization': `Bearer xxx`,
|
|
110
|
-
'AccessToken': 'xxx',
|
|
111
|
-
'Content-Type': 'multipart/form-data'
|
|
112
|
-
}),
|
|
113
|
-
data: () => ({}),
|
|
114
|
-
autoUpload: true,
|
|
115
|
-
token: ''
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
const emit = defineEmits<UploadEmits>()
|
|
119
|
-
|
|
120
|
-
// 响应式数据
|
|
121
|
-
const uploadRef = ref()
|
|
122
|
-
const fileList = ref<UploadFile[]>([])
|
|
123
|
-
const uploadingFiles = reactive(new Map<string, UploadFile>())
|
|
124
|
-
const fileMap = ref(new Map<string, any>());
|
|
125
|
-
const visible = ref(true)
|
|
126
|
-
|
|
127
|
-
// 监听 modelValue 变化
|
|
128
|
-
watch(() => props.modelValue, (newVal) => {
|
|
129
|
-
fileList.value = [...newVal]
|
|
130
|
-
}, { immediate: true, deep: true })
|
|
131
|
-
|
|
132
|
-
// 文件选择后读取
|
|
133
|
-
const onAfterRead = async ({ file }: any) => {
|
|
134
|
-
const files: AfterReadFile[] = file
|
|
135
|
-
|
|
136
|
-
for (const item of files) {
|
|
137
|
-
// before
|
|
138
|
-
if (!fileMap.value.has(item.name)) {
|
|
139
|
-
emit('before', item)
|
|
140
|
-
fileMap.value.set(item.name, item);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const uploadFile: UploadFile = {
|
|
144
|
-
url: item.url,
|
|
145
|
-
name: item.name,
|
|
146
|
-
size: item.size,
|
|
147
|
-
type: item.type,
|
|
148
|
-
progress: 0,
|
|
149
|
-
status: 'uploading'
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const fileId = generateFileId()
|
|
153
|
-
uploadingFiles.set(fileId, uploadFile)
|
|
154
|
-
|
|
155
|
-
// 添加到文件列表
|
|
156
|
-
fileList.value.push(uploadFile)
|
|
157
|
-
emitFileChange()
|
|
158
|
-
|
|
159
|
-
if (props.autoUpload) {
|
|
160
|
-
await uploadFileToServer(item, uploadFile, fileId)
|
|
161
|
-
} else {
|
|
162
|
-
uploadFile.status = 'success'
|
|
163
|
-
uploadingFiles.delete(fileId)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// 上传文件到服务器
|
|
169
|
-
const uploadFileToServer = (file: AfterReadFile, uploadFile: UploadFile, fileId: string): Promise<void> => {
|
|
170
|
-
return new Promise((resolve) => {
|
|
171
|
-
if (!props.action) {
|
|
172
|
-
// 如果没有上传地址,直接标记为成功
|
|
173
|
-
uploadFile.status = 'success'
|
|
174
|
-
uploadFile.response = { message: 'No upload action provided' }
|
|
175
|
-
uploadingFiles.delete(fileId)
|
|
176
|
-
emit('success', file, uploadFile)
|
|
177
|
-
resolve()
|
|
178
|
-
return
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const headers = {
|
|
182
|
-
'Authorization': `Bearer ${props.token}`,
|
|
183
|
-
'AccessToken': props.token,
|
|
184
|
-
// 'Content-Type': 'multipart/form-data' // Commented by Joncky becauce of error "Failed to read the request form. Missing content-type boundary."
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
uni.uploadFile({
|
|
188
|
-
url: props.action,
|
|
189
|
-
filePath: file.url,
|
|
190
|
-
name: props.name,
|
|
191
|
-
header: headers,
|
|
192
|
-
// formData: props.data,
|
|
193
|
-
success: (res) => {
|
|
194
|
-
uploadFile.response = res
|
|
195
|
-
|
|
196
|
-
try {
|
|
197
|
-
uploadFile.status = 'success'
|
|
198
|
-
uploadFile.progress = 100
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
fileList.value.splice(fileList.value.length - 1, 1, {
|
|
202
|
-
...file,
|
|
203
|
-
...res,
|
|
204
|
-
status: 'success'
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
const data = JSON.parse(res.data)
|
|
208
|
-
let url = data.data
|
|
209
|
-
if (!url && data.result) {
|
|
210
|
-
url = data.result?.url
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
emit('success', file, url)
|
|
214
|
-
} catch (error) {
|
|
215
|
-
uploadFile.status = 'error'
|
|
216
|
-
console.error(error)
|
|
217
|
-
emit('fail', file, error)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
uploadingFiles.delete(fileId)
|
|
221
|
-
resolve()
|
|
222
|
-
},
|
|
223
|
-
fail: (error) => {
|
|
224
|
-
uploadFile.status = 'error'
|
|
225
|
-
console.error(error)
|
|
226
|
-
uploadingFiles.delete(fileId)
|
|
227
|
-
emit('fail', file, error)
|
|
228
|
-
resolve()
|
|
229
|
-
}
|
|
230
|
-
})
|
|
231
|
-
})
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// 删除文件
|
|
235
|
-
const onDelete = (event: any) => {
|
|
236
|
-
const { index } = event
|
|
237
|
-
const deletedFile = fileList.value[index]
|
|
238
|
-
|
|
239
|
-
fileList.value.splice(index, 1)
|
|
240
|
-
emitFileChange()
|
|
241
|
-
emit('delete', deletedFile, index)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// 文件大小超出限制
|
|
245
|
-
const onOversize = (event: any) => {
|
|
246
|
-
const file = event
|
|
247
|
-
emit('oversize', file)
|
|
248
|
-
uni.showToast({
|
|
249
|
-
title: `文件大小不能超过 ${props.maxSize / 1024 / 1024}MB`,
|
|
250
|
-
icon: 'none'
|
|
251
|
-
})
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// 生成文件ID
|
|
255
|
-
const generateFileId = (): string => {
|
|
256
|
-
return Date.now() + '-' + Math.random().toString(36).substr(2, 9)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// 触发文件变化事件
|
|
260
|
-
const emitFileChange = () => {
|
|
261
|
-
emit('update:modelValue', fileList.value)
|
|
262
|
-
emit('change', fileList.value)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// 手动上传
|
|
266
|
-
const submit = (): Promise<UploadFile[]> => {
|
|
267
|
-
return new Promise((resolve) => {
|
|
268
|
-
const pendingFiles = fileList.value.filter(file => file.status !== 'success')
|
|
269
|
-
|
|
270
|
-
if (pendingFiles.length === 0) {
|
|
271
|
-
resolve(fileList.value)
|
|
272
|
-
return
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// 这里可以实现批量上传逻辑
|
|
276
|
-
Promise.all(pendingFiles.map(file => {
|
|
277
|
-
// 重新上传逻辑
|
|
278
|
-
return Promise.resolve(file)
|
|
279
|
-
})).then(() => {
|
|
280
|
-
resolve(fileList.value)
|
|
281
|
-
})
|
|
282
|
-
})
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// 清空文件列表
|
|
286
|
-
const clearFiles = () => {
|
|
287
|
-
fileList.value = []
|
|
288
|
-
uploadingFiles.clear()
|
|
289
|
-
emitFileChange()
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// 获取文件列表
|
|
293
|
-
const getFiles = (): UploadFile[] => {
|
|
294
|
-
return fileList.value
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// 隐藏
|
|
298
|
-
const hide = () => {
|
|
299
|
-
visible.value = false
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// 暴露方法给父组件
|
|
303
|
-
defineExpose({
|
|
304
|
-
submit,
|
|
305
|
-
clearFiles,
|
|
306
|
-
getFiles,
|
|
307
|
-
hide
|
|
308
|
-
})
|
|
309
|
-
</script>
|
package/plugins/uview-plus.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import uviewPlus from 'uview-plus'
|
|
2
|
-
|
|
3
|
-
export const UViewPlusPlugin = {
|
|
4
|
-
install(app) {
|
|
5
|
-
// 安装 uview-plus
|
|
6
|
-
app.use(uviewPlus)
|
|
7
|
-
|
|
8
|
-
// 注册一些常用的 uview-plus 组件作为全局组件
|
|
9
|
-
app.component('UButton', uviewPlus.Button)
|
|
10
|
-
app.component('UInput', uviewPlus.Input)
|
|
11
|
-
app.component('UAvatar', uviewPlus.Avatar)
|
|
12
|
-
app.component('UBadge', uviewPlus.Badge)
|
|
13
|
-
app.component('UIcon', uviewPlus.Icon)
|
|
14
|
-
app.component('UPopup', uviewPlus.Popup)
|
|
15
|
-
app.component('USearch', uviewPlus.Search)
|
|
16
|
-
app.component('UIndexList', uviewPlus.IndexList)
|
|
17
|
-
app.component('UIndexItem', uviewPlus.IndexItem)
|
|
18
|
-
app.component('UCard', uviewPlus.Card)
|
|
19
|
-
app.component('UTabs', uviewPlus.Tabs)
|
|
20
|
-
app.component('UParse', uviewPlus.Parse)
|
|
21
|
-
app.component('ULink', uviewPlus.Link)
|
|
22
|
-
app.component('UUpload', uviewPlus.Upload)
|
|
23
|
-
app.component('ULineProgress', uviewPlus.LineProgress)
|
|
24
|
-
app.component('UModal', uviewPlus.Modal)
|
|
25
|
-
app.component('UTag', uviewPlus.Tag)
|
|
26
|
-
app.component('UForm', uviewPlus.Form)
|
|
27
|
-
app.component('UFormItem', uviewPlus.FormItem)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { AllowedComponentProps, VNodeProps } from '../common'
|
|
2
|
-
|
|
3
|
-
declare interface ArrowBarProps {
|
|
4
|
-
title: string
|
|
5
|
-
icon?: string
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
declare interface _ArrowBar {
|
|
9
|
-
new(): {
|
|
10
|
-
$props: AllowedComponentProps & VNodeProps & ArrowBarProps
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export declare const ArrowBar: _ArrowBar
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { AllowedComponentProps, VNodeProps } from '../common'
|
|
2
|
-
|
|
3
|
-
declare interface UploadFile {
|
|
4
|
-
url: string
|
|
5
|
-
name?: string
|
|
6
|
-
size?: number
|
|
7
|
-
type?: string
|
|
8
|
-
progress?: number
|
|
9
|
-
status?: 'uploading' | 'success' | 'error'
|
|
10
|
-
response?: any
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
declare interface ChatUploadProps {
|
|
14
|
-
modelValue?: UploadFile[]
|
|
15
|
-
maxCount?: number
|
|
16
|
-
maxSize?: number
|
|
17
|
-
sizeType?: string[]
|
|
18
|
-
sourceType?: string[]
|
|
19
|
-
deletable?: boolean
|
|
20
|
-
previewFullImage?: boolean
|
|
21
|
-
multiple?: boolean
|
|
22
|
-
disabled?: boolean
|
|
23
|
-
customBtn?: boolean
|
|
24
|
-
showProgress?: boolean
|
|
25
|
-
uploadText?: string
|
|
26
|
-
width?: number | string
|
|
27
|
-
height?: number | string
|
|
28
|
-
action?: string
|
|
29
|
-
name?: string
|
|
30
|
-
headers?: Record<string, string>
|
|
31
|
-
data?: Record<string, any>
|
|
32
|
-
autoUpload?: boolean
|
|
33
|
-
token?: string
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
declare interface ChatUploadEmits {
|
|
37
|
-
(e: 'update:modelValue', files: UploadFile[]): void
|
|
38
|
-
(e: 'change', files: UploadFile[]): void
|
|
39
|
-
(e: 'before', file: UploadFile): void
|
|
40
|
-
(e: 'success', file: UploadFile, response: any): void
|
|
41
|
-
(e: 'fail', file: UploadFile, error: any): void
|
|
42
|
-
(e: 'progress', file: UploadFile, progress: number): void
|
|
43
|
-
(e: 'delete', file: UploadFile, index: number): void
|
|
44
|
-
(e: 'oversize', file: UploadFile): void
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
declare interface _ChatUpload {
|
|
48
|
-
new(): {
|
|
49
|
-
$props: AllowedComponentProps & VNodeProps & ChatUploadProps
|
|
50
|
-
$emit: ChatUploadEmits
|
|
51
|
-
}
|
|
52
|
-
submit: () => Promise<UploadFile[]>
|
|
53
|
-
clearFiles: () => void
|
|
54
|
-
getFiles: () => UploadFile[]
|
|
55
|
-
hide: () => void
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export declare const ChatUpload: _ChatUpload
|