tang-ui-x 1.3.4 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,8 +30,8 @@
30
30
  })
31
31
 
32
32
  // 计算图标样式
33
- const iconStyle = computed(() : string => {
34
- const styles : string[] = []
33
+ const iconStyle = computed(() : string => {
34
+ const styles : string[] = []
35
35
 
36
36
  styles.push(`font-size: ${iconSize.value}`)
37
37
  styles.push(`color: ${props.color}`)
@@ -40,9 +40,9 @@
40
40
  styles.push('font-weight: bold')
41
41
  }
42
42
 
43
- if (props.customStyle !== '') {
44
- styles.push(props.customStyle)
45
- }
43
+ if (props.customStyle !== '') {
44
+ styles.push(props.customStyle)
45
+ }
46
46
 
47
47
  return styles.join('; ')
48
48
  })
@@ -51,18 +51,18 @@
51
51
  const iconClass = computed(() : string => {
52
52
  const classes : string[] = ['inline-block', 'leading-none', 'iconfont']
53
53
 
54
- if (props.name.length > 0) {
55
- // 支持传入 icon-xxx 或直接传入 xxx
56
- if (props.name.indexOf('icon-') == 0) {
57
- classes.push(props.name)
58
- } else {
59
- classes.push(`icon-${props.name}`)
54
+ if (props.name.length > 0) {
55
+ // 支持传入 icon-xxx 或直接传入 xxx
56
+ if (props.name.indexOf('icon-') == 0) {
57
+ classes.push(props.name)
58
+ } else {
59
+ classes.push(`icon-${props.name}`)
60
60
  }
61
61
  }
62
62
 
63
- if (props.customClass !== '') {
64
- classes.push(props.customClass)
65
- }
63
+ if (props.customClass !== '') {
64
+ classes.push(props.customClass)
65
+ }
66
66
 
67
67
  return classes.join(' ')
68
68
  })
@@ -84,10 +84,10 @@
84
84
 
85
85
  <style lang="scss" scoped>
86
86
  /* 导入字体图标 */
87
- @import '@/static/font/iconfont.css';
87
+ @import '../../static/font/iconfont.css';
88
88
 
89
89
  .t-icon {
90
90
  display: inline-block;
91
91
  line-height: 1;
92
92
  }
93
- </style>
93
+ </style>
@@ -214,10 +214,20 @@
214
214
  "enName": "NavBar",
215
215
  "description": "Top navigation bar"
216
216
  },
217
- "I18nDemo": {
218
- "name": "I18n Demo",
219
- "enName": "I18n",
220
- "description": "Internationalization demo"
221
- }
222
- }
223
- }
217
+ "I18nDemo": {
218
+ "name": "I18n Demo",
219
+ "enName": "I18n",
220
+ "description": "Internationalization demo"
221
+ },
222
+ "Request": {
223
+ "name": "Request",
224
+ "enName": "Request",
225
+ "description": "HttpRequest wrapper, interceptor, and response handling demo"
226
+ },
227
+ "Utils": {
228
+ "name": "Utils",
229
+ "enName": "Utils",
230
+ "description": "Common utility functions and storage wrapper demo"
231
+ }
232
+ }
233
+ }
@@ -214,10 +214,20 @@
214
214
  "enName": "NavBar",
215
215
  "description": "顶部导航栏"
216
216
  },
217
- "I18nDemo": {
218
- "name": "多语言测试",
219
- "enName": "I18n",
220
- "description": "国际化多语言功能演示"
221
- }
222
- }
223
- }
217
+ "I18nDemo": {
218
+ "name": "多语言测试",
219
+ "enName": "I18n",
220
+ "description": "国际化多语言功能演示"
221
+ },
222
+ "Request": {
223
+ "name": "请求封装",
224
+ "enName": "Request",
225
+ "description": "HttpRequest 请求封装、拦截器与响应处理示例"
226
+ },
227
+ "Utils": {
228
+ "name": "工具方法",
229
+ "enName": "Utils",
230
+ "description": "常用工具函数与 storage 封装示例"
231
+ }
232
+ }
233
+ }
@@ -214,10 +214,20 @@
214
214
  "enName": "NavBar",
215
215
  "description": "頂部導航欄"
216
216
  },
217
- "I18nDemo": {
218
- "name": "多語言測試",
219
- "enName": "I18n",
220
- "description": "國際化多語言功能演示"
221
- }
222
- }
223
- }
217
+ "I18nDemo": {
218
+ "name": "多語言測試",
219
+ "enName": "I18n",
220
+ "description": "國際化多語言功能演示"
221
+ },
222
+ "Request": {
223
+ "name": "請求封裝",
224
+ "enName": "Request",
225
+ "description": "HttpRequest 請求封裝、攔截器與響應處理示例"
226
+ },
227
+ "Utils": {
228
+ "name": "工具方法",
229
+ "enName": "Utils",
230
+ "description": "常用工具函數與 storage 封裝示例"
231
+ }
232
+ }
233
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tang-ui-x",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "description": "UniApp X UI 组件库 - 基于 uni-app x 的移动端 UI 组件库",
5
5
  "main": "index.uts",
6
6
  "module": "index.uts",
@@ -0,0 +1 @@
1
+ export * from './modules/index'
@@ -0,0 +1,113 @@
1
+ /**
2
+ * 复制文本到剪贴板(跨平台支持)
3
+ * @param text 要复制的文本
4
+ * @param success 成功回调
5
+ * @param fail 失败回调
6
+ */
7
+ export function copyToClipboard(text: string, success?: () => void, fail?: (error?: any) => void) {
8
+ if (text == '') {
9
+ fail && fail('文本不能为空')
10
+ return
11
+ }
12
+
13
+ /* eslint-disable no-unreachable */
14
+ // #ifdef H5
15
+ try {
16
+ const textarea = document.createElement('textarea')
17
+ textarea.value = text
18
+ textarea.style.position = 'fixed'
19
+ textarea.style.top = '-9999px'
20
+ textarea.style.left = '-9999px'
21
+ document.body.appendChild(textarea)
22
+ textarea.select()
23
+ textarea.setSelectionRange(0, text.length)
24
+
25
+ const successful = document.execCommand('copy')
26
+ document.body.removeChild(textarea)
27
+
28
+ if (successful) {
29
+ uni.showToast({
30
+ title: '复制成功',
31
+ icon: 'success',
32
+ duration: 2000,
33
+ })
34
+ success && success()
35
+ } else {
36
+ throw new Error('复制失败')
37
+ }
38
+ return
39
+ } catch (error) {
40
+ console.error('H5 复制失败:', error)
41
+ uni.showToast({
42
+ title: '复制失败',
43
+ icon: 'error',
44
+ duration: 2000,
45
+ })
46
+ fail && fail(error)
47
+ return
48
+ }
49
+ // #endif
50
+
51
+ // #ifdef APP-ANDROID || APP-IOS || APP-HARMONY
52
+ uni.setClipboardData({
53
+ data: text,
54
+ success: () => {
55
+ uni.showToast({
56
+ title: '复制成功',
57
+ icon: 'success',
58
+ duration: 2000,
59
+ })
60
+ success && success()
61
+ },
62
+ fail: (error) => {
63
+ console.error('App 复制失败:', error)
64
+ uni.showToast({
65
+ title: '复制失败',
66
+ icon: 'error',
67
+ duration: 2000,
68
+ })
69
+ fail && fail(error)
70
+ },
71
+ })
72
+ return
73
+ // #endif
74
+
75
+ // #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-QQ || MP-TOUTIAO || MP-KUAISHOU
76
+ uni.setClipboardData({
77
+ data: text,
78
+ success: () => {
79
+ uni.showToast({
80
+ title: '复制成功',
81
+ icon: 'success',
82
+ duration: 2000,
83
+ })
84
+ success && success()
85
+ },
86
+ fail: (error) => {
87
+ console.error('小程序复制失败:', error)
88
+ uni.showToast({
89
+ title: '复制失败',
90
+ icon: 'error',
91
+ duration: 2000,
92
+ })
93
+ fail && fail(error)
94
+ },
95
+ })
96
+ // #endif
97
+ /* eslint-enable no-unreachable */
98
+ }
99
+
100
+ /**
101
+ * 复制文本到剪贴板(Promise 版本,跨平台支持)
102
+ * @param text 要复制的文本
103
+ * @returns Promise<void>
104
+ */
105
+ export function copyToClipboardAsync(text: string): Promise<void> {
106
+ return new Promise((resolve, reject) => {
107
+ copyToClipboard(
108
+ text,
109
+ () => resolve(),
110
+ (error) => reject(error)
111
+ )
112
+ })
113
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 格式化时间为上午/下午格式
3
+ * @param date 日期对象
4
+ * @returns 格式化后的时间字符串,如 "上午 8:10"
5
+ */
6
+ export function formatChatTime(date: Date): string {
7
+ const hours = date.getHours()
8
+ const minutes = date.getMinutes()
9
+ const period = hours < 12 ? '上午' : '下午'
10
+ const h = hours % 12 === 0 ? 12 : hours % 12
11
+ const m = minutes < 10 ? '0' + minutes : minutes.toString()
12
+ return `${period} ${h}:${m}`
13
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * 防抖函数
3
+ * @param func 要执行的函数
4
+ * @param delay 延迟时间(ms)
5
+ * @returns 防抖后的函数
6
+ */
7
+ export function debounce(
8
+ func: (...args: Array<any>) => void,
9
+ delay: number
10
+ ): (...args: Array<any>) => void {
11
+ let timer: number | null = null
12
+
13
+ return function (...args: Array<any>): void {
14
+ if (timer != null) {
15
+ clearTimeout(timer)
16
+ }
17
+ timer = setTimeout(() => {
18
+ func(...args)
19
+ }, delay)
20
+ }
21
+ }
22
+
23
+ /**
24
+ * 节流函数
25
+ * @param func 要执行的函数
26
+ * @param delay 延迟时间(ms)
27
+ * @returns 节流后的函数
28
+ */
29
+ export function throttle(
30
+ func: (...args: Array<any>) => void,
31
+ delay: number
32
+ ): (...args: Array<any>) => void {
33
+ let last: number = 0
34
+
35
+ return function (...args: Array<any>): void {
36
+ const now = Date.now()
37
+ if (now - last >= delay) {
38
+ last = now
39
+ func(...args)
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,10 @@
1
+ export * from './clipboard'
2
+ export * from './date'
3
+ export * from './function'
4
+ export * from './number'
5
+ export * from './object'
6
+ export * from './storage'
7
+ export * from './string'
8
+ export * from './type'
9
+ export * from './validate'
10
+ export * from './upload'
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 格式化数字(添加千分位分隔符)
3
+ * @param num 数字
4
+ * @returns 格式化后的字符串
5
+ */
6
+ export function formatNumber(num: number | string): string {
7
+ const text = `${num}`
8
+ const n = parseFloat(text)
9
+ if (isNaN(n)) return text
10
+
11
+ const parts = text.split('.')
12
+ const integerPart = parts[0]
13
+ const decimalPart = parts.length > 1 ? '.' + parts[1] : ''
14
+ const negative = integerPart.length > 0 && integerPart.charAt(0) == '-'
15
+ const digits = negative ? integerPart.substring(1) : integerPart
16
+ let formatted = ''
17
+ let count = 0
18
+
19
+ for (let i = digits.length - 1; i >= 0; i--) {
20
+ formatted = digits.charAt(i) + formatted
21
+ count++
22
+ if (i > 0 && count % 3 == 0) {
23
+ formatted = ',' + formatted
24
+ }
25
+ }
26
+
27
+ return (negative ? '-' : '') + formatted + decimalPart
28
+ }
29
+
30
+ /**
31
+ * 将字节转换为可读大小
32
+ * @param bytes 字节数
33
+ * @param decimals 小数位数
34
+ * @returns 可读大小字符串
35
+ */
36
+ export function formatBytes(bytes: number, decimals: number = 2): string {
37
+ if (bytes === 0) return '0 B'
38
+
39
+ const k: number = 1024
40
+ const sizes: Array<string> = ['B', 'KB', 'MB', 'GB', 'TB']
41
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
42
+
43
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))} ${sizes[i]}`
44
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * 安全获取对象属性
3
+ * @param obj 对象
4
+ * @param path 属性路径,如 'a.b.c'
5
+ * @param defaultValue 默认值
6
+ * @returns 属性值
7
+ */
8
+ export function get(obj: any, path: string, defaultValue: any | null = null): any | null {
9
+ const keys: Array<string> = path.split('.')
10
+ let result: any = obj
11
+
12
+ for (const key of keys) {
13
+ if (result == null || typeof result !== 'object') {
14
+ return defaultValue
15
+ }
16
+ result = result[key]
17
+ }
18
+
19
+ return result == null ? defaultValue : result
20
+ }
21
+
22
+ /**
23
+ * 深拷贝对象
24
+ * @param obj 要拷贝的对象
25
+ * @returns 拷贝后的对象
26
+ */
27
+ export function deepClone<T>(obj: T): T {
28
+ if (obj == null || typeof obj !== 'object') {
29
+ return obj
30
+ }
31
+
32
+ if (obj instanceof Date) {
33
+ return new Date(obj.getTime()) as any
34
+ }
35
+
36
+ if (obj instanceof Array) {
37
+ return obj.map((item) => deepClone(item)) as any
38
+ }
39
+
40
+ const source = obj as UTSJSONObject
41
+ const cloned = {} as UTSJSONObject
42
+ for (const key in source) {
43
+ cloned[key] = deepClone(source[key])
44
+ }
45
+ return cloned as T
46
+ }
47
+
48
+ /**
49
+ * 将对象转换为 Query 字符串
50
+ * @param obj 要转换的对象
51
+ * @param prefix 前缀
52
+ * @returns Query 字符串
53
+ */
54
+ export function toQueryString(obj: UTSJSONObject, prefix: string = ''): string {
55
+ const parts: Array<string> = []
56
+
57
+ for (const key in obj) {
58
+ const value = obj[key]
59
+ const fullKey = prefix != '' ? `${prefix}[${key}]` : key
60
+
61
+ if (value == null) {
62
+ parts.push(`${encodeURIComponent(fullKey)}=`)
63
+ continue
64
+ }
65
+
66
+ if (typeof value === 'object' && !Array.isArray(value)) {
67
+ parts.push(toQueryString(value as UTSJSONObject, fullKey))
68
+ } else {
69
+ parts.push(`${encodeURIComponent(fullKey)}=${encodeURIComponent(`${value}`)}`)
70
+ }
71
+ }
72
+
73
+ return parts.join('&')
74
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * 设置本地缓存
3
+ * @param key 缓存键
4
+ * @param value 缓存值
5
+ * @returns 是否设置成功
6
+ */
7
+ export function setStorage(key: string, value: any): boolean {
8
+ if (key == '') {
9
+ return false
10
+ }
11
+
12
+ try {
13
+ uni.setStorageSync(key, value)
14
+ return true
15
+ } catch (error) {
16
+ console.error('setStorage failed:', error)
17
+ return false
18
+ }
19
+ }
20
+
21
+ /**
22
+ * 获取本地缓存
23
+ * @param key 缓存键
24
+ * @param defaultValue 默认值
25
+ * @returns 缓存值或默认值
26
+ */
27
+ export function getStorage(key: string, defaultValue: any | null = null): any | null {
28
+ if (key == '') {
29
+ return defaultValue
30
+ }
31
+
32
+ try {
33
+ const value = uni.getStorageSync(key)
34
+ return value == '' ? defaultValue : value
35
+ } catch (error) {
36
+ console.error('getStorage failed:', error)
37
+ return defaultValue
38
+ }
39
+ }
40
+
41
+ /**
42
+ * 判断本地缓存是否存在
43
+ * @param key 缓存键
44
+ * @returns 是否存在
45
+ */
46
+ export function hasStorage(key: string): boolean {
47
+ if (key == '') {
48
+ return false
49
+ }
50
+
51
+ try {
52
+ const value = uni.getStorageSync(key)
53
+ return value != ''
54
+ } catch (error) {
55
+ console.error('hasStorage failed:', error)
56
+ return false
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 移除本地缓存
62
+ * @param key 缓存键
63
+ * @returns 是否移除成功
64
+ */
65
+ export function removeStorage(key: string): boolean {
66
+ if (key == '') {
67
+ return false
68
+ }
69
+
70
+ try {
71
+ uni.removeStorageSync(key)
72
+ return true
73
+ } catch (error) {
74
+ console.error('removeStorage failed:', error)
75
+ return false
76
+ }
77
+ }
78
+
79
+ /**
80
+ * 清空全部本地缓存
81
+ * @returns 是否清空成功
82
+ */
83
+ export function clearStorage(): boolean {
84
+ try {
85
+ uni.clearStorageSync()
86
+ return true
87
+ } catch (error) {
88
+ console.error('clearStorage failed:', error)
89
+ return false
90
+ }
91
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 截取字符串并添加省略号
3
+ * @param str 字符串
4
+ * @param length 最大长度
5
+ * @returns 截取后的字符串
6
+ */
7
+ export function truncate(str: string, length: number): string {
8
+ if (str == '') return ''
9
+ if (str.length <= length) return str
10
+ return str.substring(0, length) + '...'
11
+ }
12
+
13
+ /**
14
+ * 生成随机字符串
15
+ * @param length 长度
16
+ * @returns 随机字符串
17
+ */
18
+ export function randomString(length: number = 8): string {
19
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
20
+ let result = ''
21
+ for (let i = 0; i < length; i++) {
22
+ result += chars.charAt(Math.floor(Math.random() * chars.length))
23
+ }
24
+ return result
25
+ }
26
+
27
+ /**
28
+ * 生成UUID
29
+ * @returns UUID字符串
30
+ */
31
+ export function generateUUID(): string {
32
+ const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
33
+ const r = (Math.random() * 16) | 0
34
+ const v = c == 'x' ? r : (r & 0x3) | 0x8
35
+ return v.toString(16)
36
+ })
37
+ return uuid
38
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 检查值是否为空
3
+ * @param value 要检查的值
4
+ * @returns 是否为空
5
+ */
6
+ export function isEmpty(value: any): boolean {
7
+ if (value == null) return true
8
+ if (typeof value === 'string') return value.trim().length === 0
9
+ if (Array.isArray(value)) return value.length === 0
10
+ if (typeof value === 'object') {
11
+ const obj = value as UTSJSONObject
12
+ for (const key in obj) {
13
+ return false
14
+ }
15
+ return true
16
+ }
17
+ return false
18
+ }
19
+
20
+ /**
21
+ * 检查值是否有效(非空)
22
+ * @param value 要检查的值
23
+ * @returns 是否有效
24
+ */
25
+ export function isValid(value: any): boolean {
26
+ return value != null
27
+ }
28
+
29
+ /**
30
+ * 验证手机号
31
+ * @param phone 手机号
32
+ * @returns 是否有效
33
+ */
34
+ export function isValidPhone(phone: string): boolean {
35
+ const phoneRegex = /^1[3-9]\d{9}$/
36
+ return phoneRegex.test(phone)
37
+ }
38
+
39
+ /**
40
+ * 验证邮箱
41
+ * @param email 邮箱
42
+ * @returns 是否有效
43
+ */
44
+ export function isValidEmail(email: string): boolean {
45
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
46
+ return emailRegex.test(email)
47
+ }