stellar-ui-v2 1.35.3
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/common/css/common.scss +61 -0
- package/components/ste-animate/README.md +117 -0
- package/components/ste-animate/animate.scss +247 -0
- package/components/ste-animate/ste-animate.vue +200 -0
- package/components/ste-badge/README.md +171 -0
- package/components/ste-badge/ste-badge.vue +238 -0
- package/components/ste-barcode/README.md +36 -0
- package/components/ste-barcode/encode2.js +317 -0
- package/components/ste-barcode/ste-barcode.vue +213 -0
- package/components/ste-button/README.md +129 -0
- package/components/ste-button/ste-button.vue +345 -0
- package/components/ste-calendar/README.md +304 -0
- package/components/ste-calendar/self-date.js +119 -0
- package/components/ste-calendar/ste-calendar.vue +578 -0
- package/components/ste-checkbox/README.md +297 -0
- package/components/ste-checkbox/ste-checkbox.vue +305 -0
- package/components/ste-checkbox-group/ste-checkbox-group.vue +133 -0
- package/components/ste-code-input/README.md +67 -0
- package/components/ste-code-input/ste-code-input.vue +302 -0
- package/components/ste-date-picker/README.md +135 -0
- package/components/ste-date-picker/ste-date-picker.vue +407 -0
- package/components/ste-drag/README.md +103 -0
- package/components/ste-drag/ste-drag.vue +203 -0
- package/components/ste-dropdown-menu/README.md +358 -0
- package/components/ste-dropdown-menu/ste-dropdown-menu.vue +405 -0
- package/components/ste-dropdown-menu-item/ste-dropdown-menu-item.vue +176 -0
- package/components/ste-icon/README.md +90 -0
- package/components/ste-icon/iconfont.css +8 -0
- package/components/ste-icon/ste-icon.vue +147 -0
- package/components/ste-image/README.md +154 -0
- package/components/ste-image/ste-image.vue +218 -0
- package/components/ste-index-item/ste-index-item.vue +96 -0
- package/components/ste-index-list/README.md +153 -0
- package/components/ste-index-list/ste-index-list.vue +128 -0
- package/components/ste-input/README.md +146 -0
- package/components/ste-input/ste-input.vue +480 -0
- package/components/ste-loading/README.md +81 -0
- package/components/ste-loading/ste-loading.vue +166 -0
- package/components/ste-media-preview/README.md +243 -0
- package/components/ste-media-preview/TouchScaleing.js +102 -0
- package/components/ste-media-preview/ste-media-preview.vue +267 -0
- package/components/ste-message-box/README.md +217 -0
- package/components/ste-message-box/ste-message-box.js +72 -0
- package/components/ste-message-box/ste-message-box.vue +380 -0
- package/components/ste-notice-bar/README.md +129 -0
- package/components/ste-notice-bar/ste-notice-bar.vue +331 -0
- package/components/ste-number-keyboard/README.md +246 -0
- package/components/ste-number-keyboard/keyboard.vue +140 -0
- package/components/ste-number-keyboard/ste-number-keyboard.vue +240 -0
- package/components/ste-picker/ste-picker.vue +258 -0
- package/components/ste-popup/README.md +148 -0
- package/components/ste-popup/ste-popup.vue +337 -0
- package/components/ste-price/README.md +129 -0
- package/components/ste-price/ste-price.vue +258 -0
- package/components/ste-progress/README.md +87 -0
- package/components/ste-progress/ste-progress.vue +200 -0
- package/components/ste-qrcode/README.md +50 -0
- package/components/ste-qrcode/ste-qrcode.vue +164 -0
- package/components/ste-qrcode/uqrcode.js +34 -0
- package/components/ste-radio/README.md +286 -0
- package/components/ste-radio/ste-radio.vue +293 -0
- package/components/ste-radio-group/ste-radio-group.vue +128 -0
- package/components/ste-rate/README.md +115 -0
- package/components/ste-rate/ste-rate.vue +202 -0
- package/components/ste-read-more/README.md +111 -0
- package/components/ste-read-more/ste-read-more.vue +133 -0
- package/components/ste-rich-text/README.md +31 -0
- package/components/ste-rich-text/ste-rich-text.vue +70 -0
- package/components/ste-scroll-to/README.md +68 -0
- package/components/ste-scroll-to/mixin.js +173 -0
- package/components/ste-scroll-to/ste-scroll-to.vue +45 -0
- package/components/ste-scroll-to-item/ste-scroll-to-item.vue +25 -0
- package/components/ste-search/README.md +262 -0
- package/components/ste-search/ste-search.vue +547 -0
- package/components/ste-select/README.md +434 -0
- package/components/ste-select/datapager.vue +62 -0
- package/components/ste-select/datetime.vue +106 -0
- package/components/ste-select/defaultDate.js +142 -0
- package/components/ste-select/ste-select.vue +843 -0
- package/components/ste-signature/README.md +105 -0
- package/components/ste-signature/ste-signature.vue +220 -0
- package/components/ste-slider/README.md +165 -0
- package/components/ste-slider/ste-slider.vue +544 -0
- package/components/ste-step/ste-step.vue +264 -0
- package/components/ste-stepper/README.md +170 -0
- package/components/ste-stepper/ste-stepper.vue +373 -0
- package/components/ste-steps/README.md +132 -0
- package/components/ste-steps/ste-steps.vue +65 -0
- package/components/ste-sticky/README.md +52 -0
- package/components/ste-sticky/ste-sticky.vue +127 -0
- package/components/ste-swipe-action/README.md +197 -0
- package/components/ste-swipe-action/ste-swipe-action.vue +303 -0
- package/components/ste-swipe-action-group/ste-swipe-action-group.vue +104 -0
- package/components/ste-swiper/README.md +173 -0
- package/components/ste-swiper/ste-swiper.vue +462 -0
- package/components/ste-swiper-item/ste-swiper-item.vue +41 -0
- package/components/ste-switch/README.md +110 -0
- package/components/ste-switch/ste-switch.vue +144 -0
- package/components/ste-tab/ste-tab.vue +87 -0
- package/components/ste-table/README.md +785 -0
- package/components/ste-table/common.js +8 -0
- package/components/ste-table/ste-table.vue +666 -0
- package/components/ste-table/utils.js +20 -0
- package/components/ste-table-column/checkbox-icon.vue +65 -0
- package/components/ste-table-column/common.scss +65 -0
- package/components/ste-table-column/radio-icon.vue +110 -0
- package/components/ste-table-column/ste-table-column.vue +255 -0
- package/components/ste-table-column/sub-table.vue +116 -0
- package/components/ste-table-column/table-popover.vue +204 -0
- package/components/ste-table-column/var.scss +1 -0
- package/components/ste-tabs/README.md +475 -0
- package/components/ste-tabs/props.js +212 -0
- package/components/ste-tabs/ste-tabs.vue +758 -0
- package/components/ste-text/README.md +66 -0
- package/components/ste-text/ste-text.vue +72 -0
- package/components/ste-toast/README.md +243 -0
- package/components/ste-toast/ste-toast.js +69 -0
- package/components/ste-toast/ste-toast.vue +231 -0
- package/components/ste-touch-swipe/README.md +104 -0
- package/components/ste-touch-swipe/TouchEvent.js +72 -0
- package/components/ste-touch-swipe/ste-touch-swipe.vue +327 -0
- package/components/ste-touch-swipe-item/ste-touch-swipe-item.vue +33 -0
- package/components/ste-tour/README.md +194 -0
- package/components/ste-tour/ste-tour.vue +355 -0
- package/components/ste-tree/README.md +240 -0
- package/components/ste-tree/ste-tree.vue +350 -0
- package/components/ste-upload/README.md +276 -0
- package/components/ste-upload/ReadFile.js +229 -0
- package/components/ste-upload/ste-upload.vue +526 -0
- package/components/ste-video/README.md +60 -0
- package/components/ste-video/props.js +149 -0
- package/components/ste-video/ste-video.vue +647 -0
- package/config/color.js +22 -0
- package/index.js +2 -0
- package/package.json +19 -0
- package/utils/Color.js +66 -0
- package/utils/System.js +110 -0
- package/utils/dayjs.min.js +1 -0
- package/utils/mixin.js +67 -0
- package/utils/store.js +7 -0
- package/utils/utils.js +604 -0
package/utils/utils.js
ADDED
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
import Color from './Color.js';
|
|
2
|
+
import System from './System.js';
|
|
3
|
+
import dayjs from './dayjs.min.js';
|
|
4
|
+
let throLast = 0; // 节流方法用变量
|
|
5
|
+
let throTimer = null; // 节流方法用的变量
|
|
6
|
+
|
|
7
|
+
let utils = {
|
|
8
|
+
Color,
|
|
9
|
+
System,
|
|
10
|
+
dayjs,
|
|
11
|
+
/**
|
|
12
|
+
* 格式化像素单位为px
|
|
13
|
+
* @param value {Number | String} 像素单位值
|
|
14
|
+
* @param restype {"str" | "num"} 返回值类型
|
|
15
|
+
*/
|
|
16
|
+
formatPx(value, restype = 'str') {
|
|
17
|
+
let format = value ? value : 0;
|
|
18
|
+
if (format && isNaN(format)) {
|
|
19
|
+
if (/^\d+px$/i.test(format)) {
|
|
20
|
+
return restype === 'num' ? Number(format.slice(0, -2)) : format;
|
|
21
|
+
} else if (/^\d+rpx/i.test(format)) {
|
|
22
|
+
format = Number(format.slice(0, -3));
|
|
23
|
+
} else {
|
|
24
|
+
return format;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
let px = 0;
|
|
28
|
+
if (format !== 0) {
|
|
29
|
+
px = (format * System.getWindowWidth()) / 750;
|
|
30
|
+
}
|
|
31
|
+
return restype === 'num' ? px : `${px}px`;
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* 背景值转样式
|
|
35
|
+
* @param {String} value
|
|
36
|
+
*/
|
|
37
|
+
bg2style(value) {
|
|
38
|
+
const result = {};
|
|
39
|
+
const colorReg = /^(\#|rgba?)/i;
|
|
40
|
+
const colorsReg = /^linear\-gradient/i;
|
|
41
|
+
const imgReg = /^(https?\:\/\/|data\:image\/)/i;
|
|
42
|
+
if (colorReg.test(value)) {
|
|
43
|
+
// 纯色
|
|
44
|
+
result.backgroundColor = value;
|
|
45
|
+
} else if (colorsReg.test(value)) {
|
|
46
|
+
// 渐变色
|
|
47
|
+
result.backgroundImage = value;
|
|
48
|
+
} else if (imgReg.test(value)) {
|
|
49
|
+
// 图片
|
|
50
|
+
result.backgroundImage = `url(${value})`;
|
|
51
|
+
} else {
|
|
52
|
+
// 其他原生值
|
|
53
|
+
result.background = value;
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
},
|
|
57
|
+
/**兼容css中的单位
|
|
58
|
+
* 如果值为数字,则拼接 'rpx',否则直接返回字符串的值
|
|
59
|
+
*/
|
|
60
|
+
addUnit(val) {
|
|
61
|
+
let newVal;
|
|
62
|
+
if (this.isNumber(val)) {
|
|
63
|
+
newVal = val + 'rpx';
|
|
64
|
+
} else {
|
|
65
|
+
newVal = val;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// #ifdef H5
|
|
69
|
+
if (newVal && newVal.indexOf('rpx') >= 0) {
|
|
70
|
+
newVal = this.formatPx(newVal);
|
|
71
|
+
}
|
|
72
|
+
// #endif
|
|
73
|
+
return newVal;
|
|
74
|
+
},
|
|
75
|
+
/**字符串是否为数字
|
|
76
|
+
*@value 要判断的字符串
|
|
77
|
+
*/
|
|
78
|
+
isNumber(value) {
|
|
79
|
+
return !isNaN(parseFloat(value)) && isFinite(value);
|
|
80
|
+
},
|
|
81
|
+
/**分转元
|
|
82
|
+
*@val 传入的分
|
|
83
|
+
*@digits 精度,-1 不使用精度 0 保留0位小数 1 保留1位小数 2保留2位小数
|
|
84
|
+
*@defaultVal 默认值
|
|
85
|
+
*@part 取值部分 0 取全部值 1 取元部分 2取角分部分
|
|
86
|
+
*/
|
|
87
|
+
fenToYuan(val, digits = -1, defaultVal = '', part = 0) {
|
|
88
|
+
let part1 = Math.floor(val / 100); // 元部分
|
|
89
|
+
let part2 = val % 100; // 角分部分
|
|
90
|
+
let newVal = '';
|
|
91
|
+
if (val == null || val === '') {
|
|
92
|
+
newVal = defaultVal;
|
|
93
|
+
} else {
|
|
94
|
+
if (digits === -1) {
|
|
95
|
+
newVal = val / 100;
|
|
96
|
+
} else {
|
|
97
|
+
newVal = (val / 100).toFixed(digits);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
newVal = String(newVal);
|
|
101
|
+
// 取全部
|
|
102
|
+
if (part === 0) {
|
|
103
|
+
return newVal;
|
|
104
|
+
// 取元部分
|
|
105
|
+
} else if (part === 1) {
|
|
106
|
+
return newVal.split('.')[0];
|
|
107
|
+
// 取角分部分
|
|
108
|
+
} else if (part === 2) {
|
|
109
|
+
if (newVal.split('.').length > 1) {
|
|
110
|
+
return '.' + newVal.split('.')[1];
|
|
111
|
+
} else {
|
|
112
|
+
return '';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
/**节流
|
|
117
|
+
*@fn 要节流的方法
|
|
118
|
+
*@args 要节流方法的参数,如果最后一个参数是 {delay:2000},则该参数为节流时间参数,不记入方法参数
|
|
119
|
+
*/
|
|
120
|
+
thro(fn, ...args) {
|
|
121
|
+
let delay = 500;
|
|
122
|
+
let lastArgs = null;
|
|
123
|
+
if (args.length > 0) {
|
|
124
|
+
lastArgs = args[args.length - 1];
|
|
125
|
+
if (lastArgs?.delay != null) {
|
|
126
|
+
delay = lastArgs.delay;
|
|
127
|
+
args.pop();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
let now = new Date().getTime();
|
|
131
|
+
if (throLast === 0 || now - throLast > delay) {
|
|
132
|
+
clearTimeout(throTimer);
|
|
133
|
+
fn.call(this, ...args);
|
|
134
|
+
throLast = now;
|
|
135
|
+
throTimer = setTimeout(() => {
|
|
136
|
+
throLast = 0;
|
|
137
|
+
}, delay);
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
/**
|
|
141
|
+
* 防抖
|
|
142
|
+
*/
|
|
143
|
+
debounce(fn, ...args) {
|
|
144
|
+
let delay = 500;
|
|
145
|
+
let lastArgs = null;
|
|
146
|
+
if (args.length > 0) {
|
|
147
|
+
lastArgs = args[args.length - 1];
|
|
148
|
+
if (lastArgs?.delay != null) {
|
|
149
|
+
delay = lastArgs.delay;
|
|
150
|
+
args.pop();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
let timer = null;
|
|
154
|
+
return function() {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
timer = setTimeout(() => {
|
|
157
|
+
fn.call(this, ...args);
|
|
158
|
+
}, delay);
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
querySelector(selectors, component, all = false) {
|
|
163
|
+
const selectFn = all ? 'selectAll' : 'select';
|
|
164
|
+
return new Promise((resolve, reject) => {
|
|
165
|
+
try {
|
|
166
|
+
uni.createSelectorQuery()
|
|
167
|
+
.in(component)[selectFn](selectors)
|
|
168
|
+
.boundingClientRect((data) => {
|
|
169
|
+
resolve(data);
|
|
170
|
+
})
|
|
171
|
+
.exec();
|
|
172
|
+
} catch (e) {
|
|
173
|
+
reject(e);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
scrollViewX({
|
|
178
|
+
viewLeft, // 要显示的元素左侧位置
|
|
179
|
+
viewRight, // 要显示的元素右侧位置
|
|
180
|
+
boxLeft = 0, // 视图区域左侧位置
|
|
181
|
+
boxRight = System.getWindowWidth(), // 视图区域右侧位置
|
|
182
|
+
prevWidth = 0, // 前一个元素的宽度
|
|
183
|
+
nextWidth = 0, // 后一个元素的宽度
|
|
184
|
+
scrollLeft = 0, // 当前已经滑动的距离
|
|
185
|
+
}) {
|
|
186
|
+
const left = viewLeft - prevWidth;
|
|
187
|
+
const right = viewRight + nextWidth;
|
|
188
|
+
if (left < boxLeft) {
|
|
189
|
+
return (scrollLeft += left - boxLeft);
|
|
190
|
+
}
|
|
191
|
+
if (right > boxRight) {
|
|
192
|
+
return (scrollLeft += right - boxRight);
|
|
193
|
+
}
|
|
194
|
+
return scrollLeft;
|
|
195
|
+
},
|
|
196
|
+
getChildrenProps(component, chilName) {
|
|
197
|
+
let propsList = [];
|
|
198
|
+
// #ifdef MP-WEIXIN | MP-ALIPAY
|
|
199
|
+
const children = component.$children?.filter((tab) => tab.$options.name === chilName) || [];
|
|
200
|
+
children.forEach((tab) =>
|
|
201
|
+
propsList.push({
|
|
202
|
+
...tab.$props,
|
|
203
|
+
})
|
|
204
|
+
);
|
|
205
|
+
// #endif
|
|
206
|
+
// #ifdef H5
|
|
207
|
+
const children = component.$slots.default || [];
|
|
208
|
+
children.forEach((tab) =>
|
|
209
|
+
propsList.push({
|
|
210
|
+
...tab.componentOptions.propsData,
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
// #endif
|
|
214
|
+
return propsList;
|
|
215
|
+
},
|
|
216
|
+
/**深拷贝
|
|
217
|
+
/* obj 深拷贝对象
|
|
218
|
+
/* keySort 是否对字段进行排序
|
|
219
|
+
*/
|
|
220
|
+
deepClone(obj, keySort = false) {
|
|
221
|
+
if (obj === null || typeof obj !== 'object') {
|
|
222
|
+
return obj;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
let clone = Array.isArray(obj) ? [] : {};
|
|
226
|
+
|
|
227
|
+
let keys = Object.keys(obj);
|
|
228
|
+
|
|
229
|
+
if (keySort) {
|
|
230
|
+
keys.sort();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
for (let key of keys) {
|
|
234
|
+
// if (obj.hasOwnProperty(key)) {
|
|
235
|
+
clone[key] = this.deepClone(obj[key], keySort);
|
|
236
|
+
// }
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return clone;
|
|
240
|
+
},
|
|
241
|
+
/**对象深度合并
|
|
242
|
+
* 用source上的数据覆盖掉target上的数据,返回target
|
|
243
|
+
* */
|
|
244
|
+
deepMerge(target, source) {
|
|
245
|
+
// 遍历 source 的所有属性
|
|
246
|
+
for (const prop in source) {
|
|
247
|
+
// 判断是否为自身属性
|
|
248
|
+
if (source.hasOwnProperty(prop)) {
|
|
249
|
+
// 判断属性是否为对象,如果是则递归合并
|
|
250
|
+
if (typeof source[prop] === 'object' && !Array.isArray(source[prop]) && source[prop] !== null) {
|
|
251
|
+
// 如果 target 对应的属性不是对象,则新建一个空对象
|
|
252
|
+
if (typeof target[prop] !== 'object' || target[prop] === null) {
|
|
253
|
+
target[prop] = {};
|
|
254
|
+
}
|
|
255
|
+
this.deepMerge(target[prop], source[prop]);
|
|
256
|
+
} else {
|
|
257
|
+
// 如果属性不是对象,直接赋值
|
|
258
|
+
target[prop] = source[prop];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return target;
|
|
263
|
+
},
|
|
264
|
+
/**得到媒体文件类型*/
|
|
265
|
+
getMediaFileType(filePath, compatible = 1) {
|
|
266
|
+
//compatible 1 安卓或者ios 2 安卓且ios 3 安卓 4 ios
|
|
267
|
+
let filePathList = filePath.split('.');
|
|
268
|
+
let type = filePathList[filePathList.length - 1];
|
|
269
|
+
let videoType = [];
|
|
270
|
+
let audioType = [];
|
|
271
|
+
if (compatible === 1) {
|
|
272
|
+
videoType = ['mp4', 'mov', 'm4v', '3gp', 'avim', '3u8', 'webm'];
|
|
273
|
+
audioType = ['flac', 'm4a', 'ogg', 'ape', 'amr', 'wma', 'wav', 'mp3', 'mp4', 'aac', 'aiff', 'caf'];
|
|
274
|
+
}
|
|
275
|
+
if (compatible === 2) {
|
|
276
|
+
videoType = ['mp4', '3gp', 'm3u8'];
|
|
277
|
+
audioType = ['m4a', 'wav', 'mp3', 'aac'];
|
|
278
|
+
}
|
|
279
|
+
if (compatible === 3) {
|
|
280
|
+
videoType = ['mp4', '3gp', 'm3u8', 'webm'];
|
|
281
|
+
audioType = ['flac', 'm4a', 'ogg', 'ape', 'amr', 'wma', 'wav', 'mp3', 'mp4', 'aac'];
|
|
282
|
+
}
|
|
283
|
+
if (compatible === 4) {
|
|
284
|
+
videoType = ['mp4', 'mov', 'm4v', '3gp', 'avim', '3u8'];
|
|
285
|
+
audioType = ['flac', 'm4a', 'wav', 'mp3', 'aac', 'aiff', 'caf'];
|
|
286
|
+
}
|
|
287
|
+
let imageType = ['jpg', 'png', 'svg', 'webp', 'gif', 'bmp'];
|
|
288
|
+
if (videoType.includes(type)) {
|
|
289
|
+
return 'video';
|
|
290
|
+
}
|
|
291
|
+
if (audioType.includes(type)) {
|
|
292
|
+
return 'audio';
|
|
293
|
+
}
|
|
294
|
+
if (imageType.includes(type)) {
|
|
295
|
+
return 'image';
|
|
296
|
+
}
|
|
297
|
+
return -1;
|
|
298
|
+
},
|
|
299
|
+
/**延迟执行
|
|
300
|
+
* @millisecond 延迟的秒数
|
|
301
|
+
*/
|
|
302
|
+
sleep(millisecond) {
|
|
303
|
+
return new Promise((resolve) => {
|
|
304
|
+
setTimeout(() => {
|
|
305
|
+
resolve();
|
|
306
|
+
}, millisecond);
|
|
307
|
+
});
|
|
308
|
+
},
|
|
309
|
+
/**
|
|
310
|
+
* 全局唯一标识符
|
|
311
|
+
* @param {Number} len uuid的长度
|
|
312
|
+
* @param {Boolean} firstU 将返回的首字母置为"u
|
|
313
|
+
*/
|
|
314
|
+
guid(len = 32, firstU = true) {
|
|
315
|
+
let str = firstU ? 'u' : '';
|
|
316
|
+
for (let i = str.length; i < len; i++) str += Math.floor(Math.random() * 32).toString(32);
|
|
317
|
+
return str;
|
|
318
|
+
},
|
|
319
|
+
/**
|
|
320
|
+
* 替换html默认样式
|
|
321
|
+
* @html 要替换的文本
|
|
322
|
+
* @tag 标签名
|
|
323
|
+
* @checkProperties 检测的样式属性,如果存在该属性,则不添加样式,支持字符串和数组
|
|
324
|
+
* @replaceProperty 替换后的样式属性值
|
|
325
|
+
*/
|
|
326
|
+
richTextTagAddStyle(html, tag, checkProperties, replaceProperty) {
|
|
327
|
+
// 如果checkProperties为字符串,转成字符串数组
|
|
328
|
+
if (Object.prototype.toString.call(checkProperties) === '[object String]') {
|
|
329
|
+
checkProperties = [checkProperties];
|
|
330
|
+
}
|
|
331
|
+
// 构建正则表达式模式
|
|
332
|
+
var pattern = new RegExp(`<${tag}\\b((?:[^>]*\\s+)?style="[^"]*")?[^>]*>`, 'gi');
|
|
333
|
+
|
|
334
|
+
// 查找匹配的标签
|
|
335
|
+
var matches = html.match(pattern);
|
|
336
|
+
// 遍历匹配的标签并替换属性样式
|
|
337
|
+
if (matches) {
|
|
338
|
+
for (var i = 0; i < matches.length; i++) {
|
|
339
|
+
var match = matches[i];
|
|
340
|
+
var styleMatch = match.match(/style="([^"]+)"/i); // 提取标签中的style属性
|
|
341
|
+
|
|
342
|
+
if (styleMatch) {
|
|
343
|
+
var styleValue = styleMatch[1];
|
|
344
|
+
var styleProperties = styleValue.split(';');
|
|
345
|
+
|
|
346
|
+
// 检查样式属性是否符合要求
|
|
347
|
+
var hasMatchingProperty = false;
|
|
348
|
+
for (var j = 0; j < styleProperties.length; j++) {
|
|
349
|
+
var styleProperty = styleProperties[j].trim();
|
|
350
|
+
|
|
351
|
+
// 检查属性名是否完全匹配
|
|
352
|
+
var attributeName = styleProperty.split(':')[0].trim();
|
|
353
|
+
if (checkProperties.includes(attributeName)) {
|
|
354
|
+
hasMatchingProperty = true;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// 如果标签匹配且样式属性不符合要求,则替换或添加指定属性的样式
|
|
360
|
+
if (!hasMatchingProperty) {
|
|
361
|
+
var replacedMatch = match.replace(/(style="[^"]*)(")/, `$1${replaceProperty}"`);
|
|
362
|
+
html = html.replace(match, replacedMatch);
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
// 如果标签中没有样式属性,则添加指定属性的样式
|
|
366
|
+
var replacedMatch = match.replace('>', ` style="${replaceProperty}">`);
|
|
367
|
+
html = html.replace(match, replacedMatch);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// 返回替换后的HTML文本
|
|
373
|
+
return html;
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
randomArray(arr) {
|
|
377
|
+
const indexs = [];
|
|
378
|
+
arr.forEach(() => {
|
|
379
|
+
indexs.push(Math.floor(Math.random() * arr.length));
|
|
380
|
+
});
|
|
381
|
+
for (let i = 0; i < arr.length; i++) {
|
|
382
|
+
if (i === indexs[i]) continue;
|
|
383
|
+
let m = arr[i];
|
|
384
|
+
arr[i] = arr[indexs[i]];
|
|
385
|
+
arr[indexs[i]] = m;
|
|
386
|
+
}
|
|
387
|
+
return arr;
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* 格式化树形结构(返回的是深拷贝后的数据,不会修改原数据)
|
|
392
|
+
* @param {Array} tree 树形数组
|
|
393
|
+
* @param {String} valueKey 值的键
|
|
394
|
+
* @param {String} childrenKey 下级数组键
|
|
395
|
+
* @param {Object | Function} otherAttributes 为每个对象添加的属性(可以是一个方法,参数是当前节点,返回的对象属性会被添加到当前节点中)
|
|
396
|
+
*/
|
|
397
|
+
formatTree(
|
|
398
|
+
tree,
|
|
399
|
+
valueKey = 'value',
|
|
400
|
+
childrenKey = 'children',
|
|
401
|
+
otherAttributes = {},
|
|
402
|
+
parentNode = '__root__',
|
|
403
|
+
depth = 0
|
|
404
|
+
) {
|
|
405
|
+
const _formatTree = (tree, parentNode, depth) => {
|
|
406
|
+
const result = tree.map((item) => {
|
|
407
|
+
if (item[childrenKey] && item[childrenKey].length) {
|
|
408
|
+
item[childrenKey] = _formatTree(item[childrenKey], item[valueKey], depth + 1);
|
|
409
|
+
}
|
|
410
|
+
let _otherAttributes = otherAttributes;
|
|
411
|
+
if (typeof otherAttributes === 'function') {
|
|
412
|
+
_otherAttributes = _otherAttributes(item);
|
|
413
|
+
}
|
|
414
|
+
return Object.assign({
|
|
415
|
+
parentNode,
|
|
416
|
+
depth,
|
|
417
|
+
},
|
|
418
|
+
_otherAttributes,
|
|
419
|
+
item
|
|
420
|
+
);
|
|
421
|
+
});
|
|
422
|
+
return result;
|
|
423
|
+
};
|
|
424
|
+
return _formatTree(tree, parentNode, depth);
|
|
425
|
+
},
|
|
426
|
+
/**
|
|
427
|
+
* 根据value查找树中的某个节点
|
|
428
|
+
*/
|
|
429
|
+
findTreeNode(tree, value, valueKey = 'value', childrenKey = 'children') {
|
|
430
|
+
const _findTreeNode = (tree) => {
|
|
431
|
+
for (let i = 0; i < tree.length; i++) {
|
|
432
|
+
const item = tree[i];
|
|
433
|
+
if (item[valueKey] === value) {
|
|
434
|
+
return item;
|
|
435
|
+
}
|
|
436
|
+
if (item[childrenKey] && item[childrenKey].length) {
|
|
437
|
+
const result = _findTreeNode(item[childrenKey]);
|
|
438
|
+
if (result) {
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return null;
|
|
444
|
+
};
|
|
445
|
+
return _findTreeNode(tree);
|
|
446
|
+
},
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* 扁平化树形结构
|
|
450
|
+
* @param {Array} tree 树形数组
|
|
451
|
+
* @param {String} childrenKey 下级数组键
|
|
452
|
+
* @param {Function} filterFunc 回调函数,返回true或false判断是否将当前节点的下级扁平化
|
|
453
|
+
*/
|
|
454
|
+
flattenTree(tree, childrenKey = 'children', filterFunc = (node) => true) {
|
|
455
|
+
function _flatten(tree) {
|
|
456
|
+
let result = [];
|
|
457
|
+
tree.forEach((node) => {
|
|
458
|
+
result.push(node);
|
|
459
|
+
if (node[childrenKey] && node[childrenKey].length > 0 && filterFunc(node)) {
|
|
460
|
+
const nodes = _flatten(node[childrenKey]);
|
|
461
|
+
nodes.forEach((n) => result.push(n));
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
|
+
return _flatten(tree);
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* 获取树形结构中包含指定节点的所有上级节点信息
|
|
471
|
+
*/
|
|
472
|
+
getParentNodes(tree, filterFunc, valueKey = 'value', childrenKey = 'children') {
|
|
473
|
+
const flatten = this.flattenTree(this.formatTree(tree, valueKey, childrenKey), childrenKey);
|
|
474
|
+
const nodes = flatten.filter(filterFunc);
|
|
475
|
+
const findNode = (arr, value) => arr.find((n) => n[valueKey] === value);
|
|
476
|
+
|
|
477
|
+
const result = [];
|
|
478
|
+
nodes.forEach((node) => {
|
|
479
|
+
const isAdd = findNode(result, node[valueKey]);
|
|
480
|
+
if (isAdd) return;
|
|
481
|
+
const datas = [node];
|
|
482
|
+
if (node.parentNode === '__root__') return;
|
|
483
|
+
let parent = findNode(flatten, node.parentNode);
|
|
484
|
+
while (parent) {
|
|
485
|
+
const isAdd = findNode(result, parent[valueKey]);
|
|
486
|
+
if (!isAdd) datas.unshift(parent);
|
|
487
|
+
parent = findNode(flatten, parent.parentNode);
|
|
488
|
+
}
|
|
489
|
+
result.push(...datas);
|
|
490
|
+
});
|
|
491
|
+
return result;
|
|
492
|
+
},
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* 保留树形结构中的指定节点
|
|
496
|
+
*/
|
|
497
|
+
filterTree(tree, filterFunc, valueKey = 'value', childrenKey = 'children') {
|
|
498
|
+
// 先找到所有的节点包括上级节点
|
|
499
|
+
const nodes = this.getParentNodes(tree, filterFunc, valueKey, childrenKey);
|
|
500
|
+
const nodeValues = nodes.map((n) => n[valueKey]);
|
|
501
|
+
const _flatten = (tree) => {
|
|
502
|
+
const result = [];
|
|
503
|
+
tree.forEach((n) => {
|
|
504
|
+
const node = {
|
|
505
|
+
...n,
|
|
506
|
+
};
|
|
507
|
+
if (nodeValues.includes(node[valueKey])) {
|
|
508
|
+
result.push(node);
|
|
509
|
+
}
|
|
510
|
+
if (node[childrenKey] && node[childrenKey].length) {
|
|
511
|
+
node[childrenKey] = _flatten(node[childrenKey]);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
return result;
|
|
515
|
+
};
|
|
516
|
+
return _flatten(tree);
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* 将树形结构转换为多列二维数组
|
|
521
|
+
*/
|
|
522
|
+
treeToTable(tree, values = [], valueKey = 'value', childrenKey = 'children') {
|
|
523
|
+
const _flatten = (tree, depth = 0) => {
|
|
524
|
+
const result = [];
|
|
525
|
+
result.push(tree);
|
|
526
|
+
const value = values[depth];
|
|
527
|
+
let item = tree.find((item) => item[valueKey] === value);
|
|
528
|
+
item = item || tree[0];
|
|
529
|
+
if (item && item[childrenKey]) {
|
|
530
|
+
result.push(..._flatten(item[childrenKey], depth + 1));
|
|
531
|
+
}
|
|
532
|
+
return result;
|
|
533
|
+
};
|
|
534
|
+
return _flatten(tree);
|
|
535
|
+
},
|
|
536
|
+
|
|
537
|
+
isEmpty(value) {
|
|
538
|
+
return value === null || value === undefined || value === '';
|
|
539
|
+
},
|
|
540
|
+
/**
|
|
541
|
+
* 对比两个对象是否完全相等
|
|
542
|
+
* @param {Object} 比较的对象
|
|
543
|
+
* @param {Object} 比较的对象
|
|
544
|
+
* @param {ignoreKeys} 忽略比较的key
|
|
545
|
+
*/
|
|
546
|
+
deepEqual(obj1, obj2, ignoreKeys = []) {
|
|
547
|
+
if (obj1 === obj2 || (this.isEmpty(obj1) && thi.isEmpty(obj2))) {
|
|
548
|
+
return true; // 简单类型相等或引用相等
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (obj1 == null || obj2 == null || typeof obj1 !== 'object' || typeof obj2 !== 'object') {
|
|
552
|
+
return false; // 其中一个为null,或者不是对象
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const keys1 = Object.keys(obj1).filter((key) => !ignoreKeys.includes(key));
|
|
556
|
+
const keys2 = Object.keys(obj2).filter((key) => !ignoreKeys.includes(key));
|
|
557
|
+
|
|
558
|
+
if (keys1.length !== keys2.length) {
|
|
559
|
+
return false; // 忽略指定key后,对象属性数量不相等
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
for (let key of keys1) {
|
|
563
|
+
if (!obj2.hasOwnProperty(key)) {
|
|
564
|
+
return false; // obj2 没有 obj1 的属性
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (!this.deepEqual(obj1[key], obj2[key], ignoreKeys)) {
|
|
568
|
+
return false; // 递归比较子对象,忽略指定key
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return true;
|
|
573
|
+
},
|
|
574
|
+
getCanvasContext(canvasId, component) {
|
|
575
|
+
|
|
576
|
+
return new Promise((resolve, reject) => {
|
|
577
|
+
if (!canvasId || !component) reject();
|
|
578
|
+
// #ifdef H5
|
|
579
|
+
resolve(uni.createCanvasContext(canvasId, component))
|
|
580
|
+
// #endif
|
|
581
|
+
|
|
582
|
+
// 小程序平台
|
|
583
|
+
// #ifndef H5
|
|
584
|
+
const query = uni.createSelectorQuery().in(component);
|
|
585
|
+
query
|
|
586
|
+
.select(`#${canvasId}`)
|
|
587
|
+
.fields({
|
|
588
|
+
node: true,
|
|
589
|
+
size: true
|
|
590
|
+
})
|
|
591
|
+
.exec((res) => {
|
|
592
|
+
if (res[0]) {
|
|
593
|
+
const canvas = res[0].node;
|
|
594
|
+
if (!canvas) reject();
|
|
595
|
+
// const context = canvas.getContext('2d');
|
|
596
|
+
resolve(canvas)
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
// #endif
|
|
600
|
+
})
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
export default utils;
|