@tzk-design-vue2/shared 1.0.0

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/utils/dom.js ADDED
@@ -0,0 +1,134 @@
1
+ /**
2
+ * DOM 操作工具函数
3
+ * 主要用于 H5 环境,小程序环境使用 uni API
4
+ */
5
+
6
+ /**
7
+ * 获取元素信息
8
+ * @param {string} selector - 选择器
9
+ * @param {Object} context - 上下文 (Vue 组件实例)
10
+ * @returns {Promise<Object>}
11
+ */
12
+ export function getRect(selector, context) {
13
+ return new Promise(resolve => {
14
+ // #ifdef H5
15
+ const el = document.querySelector(selector);
16
+ if (el) {
17
+ const rect = el.getBoundingClientRect();
18
+ resolve(rect);
19
+ } else {
20
+ resolve(null);
21
+ }
22
+ // #endif
23
+
24
+ // #ifndef H5
25
+ uni
26
+ .createSelectorQuery()
27
+ .in(context)
28
+ .select(selector)
29
+ .boundingClientRect(rect => {
30
+ resolve(rect);
31
+ })
32
+ .exec();
33
+ // #endif
34
+ });
35
+ }
36
+
37
+ /**
38
+ * 获取所有元素信息
39
+ * @param {string} selector - 选择器
40
+ * @param {Object} context - 上下文 (Vue 组件实例)
41
+ * @returns {Promise<Array>}
42
+ */
43
+ export function getAllRect(selector, context) {
44
+ return new Promise(resolve => {
45
+ // #ifdef H5
46
+ const elements = document.querySelectorAll(selector);
47
+ const rects = Array.from(elements).map(el => el.getBoundingClientRect());
48
+ resolve(rects);
49
+ // #endif
50
+
51
+ // #ifndef H5
52
+ uni
53
+ .createSelectorQuery()
54
+ .in(context)
55
+ .selectAll(selector)
56
+ .boundingClientRect(rects => {
57
+ resolve(rects || []);
58
+ })
59
+ .exec();
60
+ // #endif
61
+ });
62
+ }
63
+
64
+ /**
65
+ * 获取滚动位置
66
+ * @returns {Promise<Object>}
67
+ */
68
+ export function getScrollOffset() {
69
+ return new Promise(resolve => {
70
+ // #ifdef H5
71
+ resolve({
72
+ scrollLeft: window.pageXOffset || document.documentElement.scrollLeft,
73
+ scrollTop: window.pageYOffset || document.documentElement.scrollTop
74
+ });
75
+ // #endif
76
+
77
+ // #ifndef H5
78
+ uni
79
+ .createSelectorQuery()
80
+ .selectViewport()
81
+ .scrollOffset(res => {
82
+ resolve(res);
83
+ })
84
+ .exec();
85
+ // #endif
86
+ });
87
+ }
88
+
89
+ /**
90
+ * 滚动到指定位置
91
+ * @param {number} scrollTop - 滚动位置
92
+ * @param {number} duration - 动画时长
93
+ */
94
+ export function scrollTo(scrollTop, duration = 300) {
95
+ // #ifdef H5
96
+ window.scrollTo({
97
+ top: scrollTop,
98
+ behavior: duration > 0 ? 'smooth' : 'auto'
99
+ });
100
+ // #endif
101
+
102
+ // #ifndef H5
103
+ uni.pageScrollTo({
104
+ scrollTop,
105
+ duration
106
+ });
107
+ // #endif
108
+ }
109
+
110
+ /**
111
+ * 锁定滚动
112
+ */
113
+ let scrollTop = 0;
114
+
115
+ export function lockScroll() {
116
+ // #ifdef H5
117
+ scrollTop = window.pageYOffset || document.documentElement.scrollTop;
118
+ document.body.style.position = 'fixed';
119
+ document.body.style.top = `-${scrollTop}px`;
120
+ document.body.style.width = '100%';
121
+ // #endif
122
+ }
123
+
124
+ /**
125
+ * 解锁滚动
126
+ */
127
+ export function unlockScroll() {
128
+ // #ifdef H5
129
+ document.body.style.position = '';
130
+ document.body.style.top = '';
131
+ document.body.style.width = '';
132
+ window.scrollTo(0, scrollTop);
133
+ // #endif
134
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * 全局错误处理器
3
+ */
4
+
5
+ import errorConfig from '../config/error';
6
+ import { warn } from './warn';
7
+
8
+ /**
9
+ * 错误类型
10
+ */
11
+ export const ErrorType = {
12
+ COMPONENT: 'component',
13
+ NETWORK: 'network',
14
+ BUSINESS: 'business',
15
+ UNKNOWN: 'unknown'
16
+ };
17
+
18
+ /**
19
+ * 错误日志存储
20
+ */
21
+ const errorLogs = [];
22
+
23
+ /**
24
+ * 处理错误
25
+ * @param {Error} err - 错误对象
26
+ * @param {Object} vm - Vue 实例
27
+ * @param {string} info - 错误信息
28
+ */
29
+ export function handleError(err, vm, info) {
30
+ const errorLog = {
31
+ type: ErrorType.COMPONENT,
32
+ error: err,
33
+ vm,
34
+ info,
35
+ timestamp: new Date().toISOString()
36
+ };
37
+
38
+ // 添加到错误日志
39
+ errorLogs.push(errorLog);
40
+
41
+ // 开发环境打印错误
42
+ if (process.env.NODE_ENV === 'development') {
43
+ console.error('[TZK Error]', err);
44
+ console.error('[TZK Error Info]', info);
45
+ console.error('[TZK Error VM]', vm);
46
+ }
47
+
48
+ // 错误上报
49
+ if (errorConfig.enableReport && shouldReport(errorLog)) {
50
+ reportError(errorLog);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * 处理网络错误
56
+ * @param {Error} err - 错误对象
57
+ */
58
+ export function handleNetworkError(err) {
59
+ const errorLog = {
60
+ type: ErrorType.NETWORK,
61
+ error: err,
62
+ timestamp: new Date().toISOString()
63
+ };
64
+
65
+ errorLogs.push(errorLog);
66
+
67
+ if (process.env.NODE_ENV === 'development') {
68
+ console.error('[TZK Network Error]', err);
69
+ }
70
+
71
+ if (errorConfig.enableReport && shouldReport(errorLog)) {
72
+ reportError(errorLog);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * 处理业务错误
78
+ * @param {Error} err - 错误对象
79
+ * @param {Object} context - 上下文
80
+ */
81
+ export function handleBusinessError(err, context) {
82
+ const errorLog = {
83
+ type: ErrorType.BUSINESS,
84
+ error: err,
85
+ context,
86
+ timestamp: new Date().toISOString()
87
+ };
88
+
89
+ errorLogs.push(errorLog);
90
+
91
+ if (process.env.NODE_ENV === 'development') {
92
+ console.error('[TZK Business Error]', err);
93
+ console.error('[TZK Business Context]', context);
94
+ }
95
+
96
+ if (errorConfig.enableReport && shouldReport(errorLog)) {
97
+ reportError(errorLog);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * 判断是否应该上报
103
+ * @param {Object} errorLog - 错误日志
104
+ * @returns {boolean}
105
+ */
106
+ function shouldReport(errorLog) {
107
+ // 过滤规则
108
+ if (errorConfig.filterRules && errorConfig.filterRules.length > 0) {
109
+ const shouldFilter = errorConfig.filterRules.some(rule => {
110
+ if (typeof rule === 'function') {
111
+ return rule(errorLog);
112
+ }
113
+ return false;
114
+ });
115
+
116
+ if (shouldFilter) return false;
117
+ }
118
+
119
+ // 采样率
120
+ if (errorConfig.sampleRate < 1) {
121
+ return Math.random() < errorConfig.sampleRate;
122
+ }
123
+
124
+ return true;
125
+ }
126
+
127
+ /**
128
+ * 上报错误
129
+ * @param {Object} errorLog - 错误日志
130
+ */
131
+ function reportError(errorLog) {
132
+ if (!errorConfig.reportUrl) {
133
+ warn('Error report URL is not configured');
134
+ return;
135
+ }
136
+
137
+ // 构建上报数据
138
+ const reportData = {
139
+ type: errorLog.type,
140
+ message: errorLog.error.message,
141
+ stack: errorLog.error.stack,
142
+ info: errorLog.info,
143
+ timestamp: errorLog.timestamp,
144
+ userAgent: navigator.userAgent || '',
145
+ url: window.location?.href || ''
146
+ };
147
+
148
+ // 发送上报请求
149
+ try {
150
+ // #ifdef H5
151
+ fetch(errorConfig.reportUrl, {
152
+ method: 'POST',
153
+ headers: {
154
+ 'Content-Type': 'application/json'
155
+ },
156
+ body: JSON.stringify(reportData)
157
+ }).catch(err => {
158
+ console.error('[TZK Error Report Failed]', err);
159
+ });
160
+ // #endif
161
+
162
+ // #ifndef H5
163
+ uni.request({
164
+ url: errorConfig.reportUrl,
165
+ method: 'POST',
166
+ data: reportData,
167
+ fail: err => {
168
+ console.error('[TZK Error Report Failed]', err);
169
+ }
170
+ });
171
+ // #endif
172
+ } catch (err) {
173
+ console.error('[TZK Error Report Exception]', err);
174
+ }
175
+ }
176
+
177
+ /**
178
+ * 获取错误日志
179
+ * @param {number} limit - 限制数量
180
+ * @returns {Array}
181
+ */
182
+ export function getErrorLogs(limit = 100) {
183
+ return errorLogs.slice(-limit);
184
+ }
185
+
186
+ /**
187
+ * 清空错误日志
188
+ */
189
+ export function clearErrorLogs() {
190
+ errorLogs.length = 0;
191
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * 格式化工具函数
3
+ */
4
+
5
+ /**
6
+ * 数字格式化
7
+ * @param {number} num - 数字
8
+ * @param {number} precision - 精度
9
+ * @returns {string}
10
+ */
11
+ export function formatNumber(num, precision = 2) {
12
+ if (typeof num !== 'number' || isNaN(num)) return '0';
13
+ return num.toFixed(precision);
14
+ }
15
+
16
+ /**
17
+ * 千分位格式化
18
+ * @param {number} num - 数字
19
+ * @returns {string}
20
+ */
21
+ export function formatThousands(num) {
22
+ if (typeof num !== 'number' || isNaN(num)) return '0';
23
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
24
+ }
25
+
26
+ /**
27
+ * 金额格式化
28
+ * @param {number} amount - 金额
29
+ * @param {number} precision - 精度
30
+ * @param {string} symbol - 货币符号
31
+ * @returns {string}
32
+ */
33
+ export function formatMoney(amount, precision = 2, symbol = '¥') {
34
+ if (typeof amount !== 'number' || isNaN(amount)) return `${symbol}0.00`;
35
+ const formatted = formatThousands(amount.toFixed(precision));
36
+ return `${symbol}${formatted}`;
37
+ }
38
+
39
+ /**
40
+ * 日期格式化
41
+ * @param {Date|string|number} date - 日期
42
+ * @param {string} format - 格式 (YYYY-MM-DD HH:mm:ss)
43
+ * @returns {string}
44
+ */
45
+ export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') {
46
+ const d = new Date(date);
47
+ if (isNaN(d.getTime())) return '';
48
+
49
+ const year = d.getFullYear();
50
+ const month = String(d.getMonth() + 1).padStart(2, '0');
51
+ const day = String(d.getDate()).padStart(2, '0');
52
+ const hour = String(d.getHours()).padStart(2, '0');
53
+ const minute = String(d.getMinutes()).padStart(2, '0');
54
+ const second = String(d.getSeconds()).padStart(2, '0');
55
+
56
+ return format
57
+ .replace('YYYY', year)
58
+ .replace('MM', month)
59
+ .replace('DD', day)
60
+ .replace('HH', hour)
61
+ .replace('mm', minute)
62
+ .replace('ss', second);
63
+ }
64
+
65
+ /**
66
+ * 相对时间格式化
67
+ * @param {Date|string|number} date - 日期
68
+ * @returns {string}
69
+ */
70
+ export function formatRelativeTime(date) {
71
+ const d = new Date(date);
72
+ if (isNaN(d.getTime())) return '';
73
+
74
+ const now = new Date();
75
+ const diff = now.getTime() - d.getTime();
76
+ const seconds = Math.floor(diff / 1000);
77
+ const minutes = Math.floor(seconds / 60);
78
+ const hours = Math.floor(minutes / 60);
79
+ const days = Math.floor(hours / 24);
80
+
81
+ if (seconds < 60) return '刚刚';
82
+ if (minutes < 60) return `${minutes}分钟前`;
83
+ if (hours < 24) return `${hours}小时前`;
84
+ if (days < 7) return `${days}天前`;
85
+ return formatDate(date, 'YYYY-MM-DD');
86
+ }
87
+
88
+ /**
89
+ * 文件大小格式化
90
+ * @param {number} bytes - 字节数
91
+ * @param {number} precision - 精度
92
+ * @returns {string}
93
+ */
94
+ export function formatFileSize(bytes, precision = 2) {
95
+ if (typeof bytes !== 'number' || bytes < 0) return '0 B';
96
+
97
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
98
+ let index = 0;
99
+ let size = bytes;
100
+
101
+ while (size >= 1024 && index < units.length - 1) {
102
+ size /= 1024;
103
+ index++;
104
+ }
105
+
106
+ return `${size.toFixed(precision)} ${units[index]}`;
107
+ }
108
+
109
+ /**
110
+ * 手机号格式化 (中间4位隐藏)
111
+ * @param {string} phone - 手机号
112
+ * @returns {string}
113
+ */
114
+ export function formatPhone(phone) {
115
+ if (!phone || typeof phone !== 'string') return '';
116
+ return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
117
+ }
118
+
119
+ /**
120
+ * 身份证号格式化 (中间部分隐藏)
121
+ * @param {string} idCard - 身份证号
122
+ * @returns {string}
123
+ */
124
+ export function formatIdCard(idCard) {
125
+ if (!idCard || typeof idCard !== 'string') return '';
126
+ return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2');
127
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * 性能优化工具函数
3
+ */
4
+
5
+ /**
6
+ * 防抖函数
7
+ * @param {Function} func - 需要防抖的函数
8
+ * @param {number} wait - 等待时间(ms)
9
+ * @param {boolean} immediate - 是否立即执行
10
+ * @returns {Function}
11
+ */
12
+ export function debounce(func, wait = 300, immediate = false) {
13
+ let timeout;
14
+
15
+ return function (...args) {
16
+ const context = this;
17
+
18
+ const later = () => {
19
+ timeout = null;
20
+ if (!immediate) func.apply(context, args);
21
+ };
22
+
23
+ const callNow = immediate && !timeout;
24
+
25
+ clearTimeout(timeout);
26
+ timeout = setTimeout(later, wait);
27
+
28
+ if (callNow) func.apply(context, args);
29
+ };
30
+ }
31
+
32
+ /**
33
+ * 节流函数
34
+ * @param {Function} func - 需要节流的函数
35
+ * @param {number} wait - 等待时间(ms)
36
+ * @param {Object} options - 配置项
37
+ * @param {boolean} options.leading - 是否在开始时执行
38
+ * @param {boolean} options.trailing - 是否在结束时执行
39
+ * @returns {Function}
40
+ */
41
+ export function throttle(func, wait = 300, options = {}) {
42
+ let timeout;
43
+ let previous = 0;
44
+ const { leading = true, trailing = true } = options;
45
+
46
+ return function (...args) {
47
+ const context = this;
48
+ const now = Date.now();
49
+
50
+ if (!previous && !leading) previous = now;
51
+
52
+ const remaining = wait - (now - previous);
53
+
54
+ if (remaining <= 0 || remaining > wait) {
55
+ if (timeout) {
56
+ clearTimeout(timeout);
57
+ timeout = null;
58
+ }
59
+ previous = now;
60
+ func.apply(context, args);
61
+ } else if (!timeout && trailing) {
62
+ timeout = setTimeout(() => {
63
+ previous = leading ? Date.now() : 0;
64
+ timeout = null;
65
+ func.apply(context, args);
66
+ }, remaining);
67
+ }
68
+ };
69
+ }
70
+
71
+ /**
72
+ * 请求动画帧节流
73
+ * @param {Function} func - 需要节流的函数
74
+ * @returns {Function}
75
+ */
76
+ export function rafThrottle(func) {
77
+ let rafId = null;
78
+
79
+ return function (...args) {
80
+ const context = this;
81
+
82
+ if (rafId) return;
83
+
84
+ rafId = requestAnimationFrame(() => {
85
+ func.apply(context, args);
86
+ rafId = null;
87
+ });
88
+ };
89
+ }
90
+
91
+ /**
92
+ * 延迟执行
93
+ * @param {number} ms - 延迟时间(ms)
94
+ * @returns {Promise}
95
+ */
96
+ export function sleep(ms) {
97
+ return new Promise(resolve => setTimeout(resolve, ms));
98
+ }
99
+
100
+ /**
101
+ * 函数缓存
102
+ * @param {Function} func - 需要缓存的函数
103
+ * @returns {Function}
104
+ */
105
+ export function memoize(func) {
106
+ const cache = new Map();
107
+
108
+ return function (...args) {
109
+ const key = JSON.stringify(args);
110
+
111
+ if (cache.has(key)) {
112
+ return cache.get(key);
113
+ }
114
+
115
+ const result = func.apply(this, args);
116
+ cache.set(key, result);
117
+ return result;
118
+ };
119
+ }
120
+
121
+ /**
122
+ * 函数只执行一次
123
+ * @param {Function} func - 需要执行的函数
124
+ * @returns {Function}
125
+ */
126
+ export function once(func) {
127
+ let called = false;
128
+ let result;
129
+
130
+ return function (...args) {
131
+ if (!called) {
132
+ called = true;
133
+ result = func.apply(this, args);
134
+ }
135
+ return result;
136
+ };
137
+ }