@ganwei-web/ganwei-pc-cli 6.2.9 → 6.3.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.
Files changed (71) hide show
  1. package/ganwei-iotcenter-index-6.2.3/configuration/moduleConfiguration.json +2 -0
  2. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/env.d.ts +8 -5
  3. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/index.html +1 -1
  4. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/ElementPlusAdapter.css +437 -68
  5. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/reset-6-1.css +1 -0
  6. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/reset-plus.css +396 -0
  7. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/http/createAxios.js +38 -13
  8. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/js/getLanguage.js +19 -12
  9. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/themes/dark-6-1.css +3 -3
  10. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/themes/green-6-1.css +12 -11
  11. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/App.vue +11 -6
  12. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/equipAlarmDialog/index.vue +6 -0
  13. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/expirationReminder/index.vue +7 -3
  14. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/index.scss +4 -0
  15. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.js +35 -3
  16. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.scss +7 -1
  17. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.vue +2 -2
  18. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Navigation/ContractMenu.vue +11 -37
  19. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Navigation/TopNav.vue +3 -2
  20. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Sidebar/LeftContent/index.vue +7 -1
  21. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/router.js +120 -18
  22. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/date.ts +80 -0
  23. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/dom.ts +99 -0
  24. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/env.ts +20 -0
  25. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/file.ts +74 -0
  26. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/index.ts +26 -0
  27. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/number.ts +83 -0
  28. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/performance.ts +69 -0
  29. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/storage.ts +80 -0
  30. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/string.ts +116 -0
  31. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/xss-filter.ts +260 -0
  32. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/views/jumpIframe/index.vue +45 -28
  33. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/tsconfig.json +1 -0
  34. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/vite.config.ts +10 -1
  35. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/public/static/http/createAxios.js +40 -15
  36. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/public/static/js/getLanguage.js +10 -2
  37. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/src/views/login.vue +55 -53
  38. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/src/views/ssoLogin.vue +10 -9
  39. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.development +0 -3
  40. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.production +1 -4
  41. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.test +0 -3
  42. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.eslintrc.cjs +2 -2
  43. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/env.d.ts +9 -5
  44. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/index.html +1 -0
  45. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/package.json +3 -2
  46. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/public/static/http/createAxios.js +38 -13
  47. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/public/static/js/getLanguage.js +10 -2
  48. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/App.vue +1 -1
  49. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/api.ts +0 -1
  50. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/models/response/template.ts +0 -0
  51. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/date.ts +79 -0
  52. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/dom.ts +99 -0
  53. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/env.ts +20 -0
  54. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/file.ts +74 -0
  55. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/index.ts +29 -0
  56. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/number.ts +83 -0
  57. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/performance.ts +69 -0
  58. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/signalr.ts +564 -0
  59. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/storage.ts +80 -0
  60. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/string.ts +116 -0
  61. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/xss-filter.ts +260 -0
  62. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/views/template.vue +0 -1
  63. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/tsconfig.json +7 -0
  64. package/ganwei-iotcenter-index-6.2.3/pnpm-lock.yaml +489 -155
  65. package/package.json +1 -1
  66. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/mixins/judgePermission.js +0 -60
  67. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/setStorage.js +0 -5
  68. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/setStorage.js +0 -5
  69. /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/build/{enteryJson.js → entryJson.js} +0 -0
  70. /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/{request/models/request/index.ts → enum/template.ts} +0 -0
  71. /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/models/{response/index.ts → request/template.ts} +0 -0
@@ -0,0 +1,80 @@
1
+ import dayjs from 'dayjs'
2
+ import relativeTime from 'dayjs/plugin/relativeTime'
3
+
4
+ import 'dayjs/locale/zh-cn'
5
+
6
+ dayjs.extend(relativeTime)
7
+ dayjs.locale('zh-cn')
8
+
9
+ /**
10
+ * 格式化日期
11
+ * @param date - 日期对象、时间戳或日期字符串
12
+ * @param format - 格式化模板,默认 'YYYY-MM-DD HH:mm:ss'
13
+ * @returns 格式化后的日期字符串
14
+ * @example
15
+ * formatDate(new Date()) // '2024-01-15 14:30:00'
16
+ * formatDate(Date.now(), 'YYYY年MM月DD日') // '2024年01月15日'
17
+ */
18
+ export function formatDate(
19
+ date: Date | number | string,
20
+ format = 'YYYY-MM-DD HH:mm:ss'
21
+ ): string {
22
+ return dayjs(date).format(format)
23
+ }
24
+
25
+ /**
26
+ * 获取相对时间
27
+ * @param date - 日期对象、时间戳或日期字符串
28
+ * @returns 相对时间描述,如 '3 天前'
29
+ * @example
30
+ * getRelativeTime(Date.now() - 3 * 24 * 60 * 60 * 1000) // '3 天前'
31
+ */
32
+ export function getRelativeTime(date: Date | number | string): string {
33
+ return dayjs(date).fromNow()
34
+ }
35
+
36
+ /**
37
+ * 获取时间范围
38
+ * @param type - 时间范围类型
39
+ * @returns [开始时间, 结束时间]
40
+ */
41
+ export function getDateRange(
42
+ type: 'today' | 'week' | 'month' | 'year'
43
+ ): [Date, Date] {
44
+ const now = dayjs()
45
+ let start: dayjs.Dayjs
46
+ const end: dayjs.Dayjs = now
47
+
48
+ switch (type) {
49
+ case 'today':
50
+ start = now.startOf('day')
51
+ break
52
+ case 'week':
53
+ start = now.startOf('week')
54
+ break
55
+ case 'month':
56
+ start = now.startOf('month')
57
+ break
58
+ case 'year':
59
+ start = now.startOf('year')
60
+ break
61
+ default:
62
+ start = now.startOf('day')
63
+ }
64
+
65
+ return [start.toDate(), end.toDate()]
66
+ }
67
+
68
+ /**
69
+ * 判断是否为今天
70
+ */
71
+ export function isToday(date: Date | number | string): boolean {
72
+ return dayjs(date).isSame(dayjs(), 'day')
73
+ }
74
+
75
+ /**
76
+ * 判断是否为本周
77
+ */
78
+ export function isThisWeek(date: Date | number | string): boolean {
79
+ return dayjs(date).isSame(dayjs(), 'week')
80
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * 添加事件监听器
3
+ * @returns 移除监听器的函数
4
+ */
5
+ export function addEventListener(
6
+ element: HTMLElement | Window,
7
+ event: string,
8
+ handler: EventListener,
9
+ options?: AddEventListenerOptions
10
+ ): () => void {
11
+ element.addEventListener(event, handler, options)
12
+ return () => element.removeEventListener(event, handler, options)
13
+ }
14
+
15
+ /**
16
+ * 判断元素是否可见
17
+ */
18
+ export function isVisible(element: HTMLElement): boolean {
19
+ const rect = element.getBoundingClientRect()
20
+ return (
21
+ rect.width > 0 &&
22
+ rect.height > 0 &&
23
+ rect.top < window.innerHeight &&
24
+ rect.bottom > 0 &&
25
+ rect.left < window.innerWidth &&
26
+ rect.right > 0
27
+ )
28
+ }
29
+
30
+ /**
31
+ * 滚动到元素
32
+ */
33
+ export function scrollToElement(
34
+ element: HTMLElement,
35
+ options?: ScrollIntoViewOptions
36
+ ): void {
37
+ element.scrollIntoView({
38
+ behavior: 'smooth',
39
+ block: 'start',
40
+ ...options
41
+ })
42
+ }
43
+
44
+ /**
45
+ * 复制文本到剪贴板
46
+ */
47
+ export async function copyToClipboard(text: string): Promise<boolean> {
48
+ try {
49
+ await navigator.clipboard.writeText(text)
50
+ return true
51
+ } catch {
52
+ // 降级方案
53
+ const textarea = document.createElement('textarea')
54
+ textarea.value = text
55
+ textarea.style.position = 'fixed'
56
+ textarea.style.opacity = '0'
57
+ document.body.appendChild(textarea)
58
+ textarea.select()
59
+ const success = document.execCommand('copy')
60
+ document.body.removeChild(textarea)
61
+ return success
62
+ }
63
+ }
64
+
65
+ /**
66
+ * 全屏切换
67
+ */
68
+ export function toggleFullscreen(element?: HTMLElement): Promise<void> {
69
+ const target = element || document.documentElement
70
+
71
+ if (!document.fullscreenElement) {
72
+ return target.requestFullscreen()
73
+ }
74
+ return document.exitFullscreen()
75
+
76
+ }
77
+
78
+ /**
79
+ * 获取元素样式
80
+ */
81
+ export function getStyle(element: HTMLElement, property: string): string {
82
+ return window.getComputedStyle(element).getPropertyValue(property)
83
+ }
84
+
85
+ /**
86
+ * 设置 CSS 变量
87
+ */
88
+ export function setCssVariable(name: string, value: string): void {
89
+ document.documentElement.style.setProperty(name, value)
90
+ }
91
+
92
+ /**
93
+ * 设置主题
94
+ */
95
+ export const setTheme = (theme: string): void => {
96
+ localStorage.setItem('theme', theme)
97
+ sessionStorage.setItem('theme', theme)
98
+ window.document.documentElement.setAttribute('data-theme', theme)
99
+ }
@@ -0,0 +1,20 @@
1
+ export const ENV = {
2
+
3
+ /** 应用标题 */
4
+ APP_TITLE: import.meta.env.VITE_APP_TITLE,
5
+
6
+ /** 应用端口 */
7
+ APP_VERSION: import.meta.env.VITE_APP_PORT,
8
+
9
+ /** proxy代理配置 */
10
+ APP_ENV: import.meta.env.VITE_APP_TARGET_URL,
11
+
12
+ /** 本地地址 */
13
+ API_URL: import.meta.env.VITE_APP_THIS_URL,
14
+
15
+ /** 是否开发环境 */
16
+ IS_DEV: import.meta.env.DEV,
17
+
18
+ /** 是否生产环境 */
19
+ IS_PROD: import.meta.env.PROD
20
+ } as const
@@ -0,0 +1,74 @@
1
+ /**
2
+ * 下载文件
3
+ */
4
+ export function downloadFile(url: string, filename?: string): void {
5
+ const link = document.createElement('a')
6
+ link.href = url
7
+ link.download = filename || ''
8
+ document.body.appendChild(link)
9
+ link.click()
10
+ document.body.removeChild(link)
11
+ }
12
+
13
+ /**
14
+ * 下载 Blob 文件
15
+ */
16
+ export function downloadBlob(blob: Blob, filename: string): void {
17
+ const url = URL.createObjectURL(blob)
18
+ downloadFile(url, filename)
19
+ URL.revokeObjectURL(url)
20
+ }
21
+
22
+ /**
23
+ * 文件转 Base64
24
+ */
25
+ export function fileToBase64(file: File): Promise<string> {
26
+ return new Promise((resolve, reject) => {
27
+ const reader = new FileReader()
28
+ reader.onload = () => resolve(reader.result as string)
29
+ reader.onerror = reject
30
+ reader.readAsDataURL(file)
31
+ })
32
+ }
33
+
34
+ /**
35
+ * Base64 转 Blob
36
+ */
37
+ export function base64ToBlob(
38
+ base64: string,
39
+ mimeType = 'application/octet-stream'
40
+ ): Blob {
41
+ const byteString = atob(base64.split(',')[1])
42
+ const arrayBuffer = new ArrayBuffer(byteString.length)
43
+ const uint8Array = new Uint8Array(arrayBuffer)
44
+
45
+ for (let i = 0; i < byteString.length; i++) {
46
+ uint8Array[i] = byteString.charCodeAt(i)
47
+ }
48
+
49
+ return new Blob([uint8Array], { type: mimeType })
50
+ }
51
+
52
+ /**
53
+ * 获取文件扩展名
54
+ */
55
+ export function getFileExtension(filename: string): string {
56
+ return filename.slice(((filename.lastIndexOf('.') - 1) >>> 0) + 2)
57
+ }
58
+
59
+ /**
60
+ * 验证文件类型
61
+ */
62
+ export function validateFileType(file: File, allowedTypes: string[]): boolean {
63
+ const extension = getFileExtension(file.name).toLowerCase()
64
+ return allowedTypes.includes(extension) || allowedTypes.includes(file.type)
65
+ }
66
+
67
+ /**
68
+ * 验证文件大小
69
+ * @param file - 文件对象
70
+ * @param maxSize - 最大大小(MB)
71
+ */
72
+ export function validateFileSize(file: File, maxSize: number): boolean {
73
+ return file.size <= maxSize * 1024 * 1024
74
+ }
@@ -0,0 +1,26 @@
1
+ // 日期处理
2
+ export * from './date'
3
+
4
+ // 数字处理
5
+ export * from './number'
6
+
7
+ // 字符串处理
8
+ export * from './string'
9
+
10
+ // 文件处理
11
+ export * from './file'
12
+
13
+ // DOM 操作
14
+ export * from './dom'
15
+
16
+ // 本地存储
17
+ export * from './storage'
18
+
19
+ // 性能优化
20
+ export * from './performance'
21
+
22
+ // 环境变量
23
+ export * from './env'
24
+
25
+ // XSS 过滤
26
+ export * from './xss-filter'
@@ -0,0 +1,83 @@
1
+ /**
2
+ * 格式化数字(千分位)
3
+ * @param num - 数字或字符串
4
+ * @param decimals - 小数位数,默认 0
5
+ * @returns 格式化后的数字字符串
6
+ */
7
+ export function formatNumber(num: number | string, decimals = 0): string {
8
+ const number = Number(num)
9
+ if (isNaN(number)) return '0'
10
+
11
+ return number.toLocaleString('zh-CN', {
12
+ minimumFractionDigits: decimals,
13
+ maximumFractionDigits: decimals
14
+ })
15
+ }
16
+
17
+ /**
18
+ * 格式化金额
19
+ * @param amount - 金额
20
+ * @param currency - 货币符号,默认 '¥'
21
+ * @param decimals - 小数位数,默认 2
22
+ */
23
+ export function formatMoney(
24
+ amount: number | string,
25
+ currency = '¥',
26
+ decimals = 2
27
+ ): string {
28
+ const number = Number(amount)
29
+ if (isNaN(number)) return `${currency}0.00`
30
+
31
+ return `${currency}${formatNumber(number, decimals)}`
32
+ }
33
+
34
+ /**
35
+ * 格式化百分比
36
+ * @param value - 值(0-1)
37
+ * @param decimals - 小数位数,默认 2
38
+ */
39
+ export function formatPercent(
40
+ value: number | string,
41
+ decimals = 2
42
+ ): string {
43
+ const number = Number(value)
44
+ if (isNaN(number)) return '0%'
45
+
46
+ return `${(number * 100).toFixed(decimals)}%`
47
+ }
48
+
49
+ /**
50
+ * 格式化文件大小
51
+ * @param bytes - 字节数
52
+ * @param decimals - 小数位数,默认 2
53
+ */
54
+ export function formatFileSize(bytes: number, decimals = 2): string {
55
+ if (bytes === 0) return '0 B'
56
+
57
+ const k = 1024
58
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
59
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
60
+
61
+ return `${(bytes / Math.pow(k, i)).toFixed(decimals)} ${sizes[i]}`
62
+ }
63
+
64
+ /**
65
+ * 数字范围限制
66
+ */
67
+ export function clamp(value: number, min: number, max: number): number {
68
+ return Math.min(Math.max(value, min), max)
69
+ }
70
+
71
+ /**
72
+ * 生成随机数
73
+ */
74
+ export function random(min: number, max: number): number {
75
+ return Math.floor(Math.random() * (max - min + 1)) + min
76
+ }
77
+
78
+ /**
79
+ * 生成随机 ID
80
+ */
81
+ export function generateId(prefix = ''): string {
82
+ return `${prefix}${Date.now()}${Math.random().toString(36).substr(2, 9)}`
83
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * 防抖函数
3
+ * @param func - 要防抖的函数
4
+ * @param wait - 等待时间(毫秒)
5
+ * @param immediate - 是否立即执行
6
+ */
7
+ export function debounce<T extends (...args: any[]) => any>(
8
+ func: T,
9
+ wait: number,
10
+ immediate = false
11
+ ): (...args: Parameters<T>) => ReturnType<T> | undefined {
12
+ let timeout: ReturnType<typeof setTimeout> | null = null
13
+
14
+ return function (this: any, ...args: Parameters<T>) {
15
+ const context = this
16
+
17
+ const later = () => {
18
+ timeout = null
19
+ if (!immediate) {
20
+ func.apply(context, args)
21
+ }
22
+ }
23
+
24
+ const callNow = immediate && !timeout
25
+
26
+ if (timeout) {
27
+ clearTimeout(timeout)
28
+ }
29
+
30
+ timeout = setTimeout(later, wait)
31
+
32
+ if (callNow) {
33
+ func.apply(context, args)
34
+ }
35
+ }
36
+ }
37
+
38
+ /**
39
+ * 节流函数
40
+ * @param func - 要节流的函数
41
+ * @param wait - 等待时间(毫秒)
42
+ */
43
+ export function throttle<T extends (...args: any[]) => any>(
44
+ func: T,
45
+ wait: number
46
+ ): (...args: Parameters<T>) => ReturnType<T> | undefined {
47
+ let timeout: ReturnType<typeof setTimeout> | null = null
48
+ let previous = 0
49
+
50
+ return function (this: any, ...args: Parameters<T>) {
51
+ const context = this
52
+ const now = Date.now()
53
+
54
+ if (now - previous > wait) {
55
+ if (timeout) {
56
+ clearTimeout(timeout)
57
+ timeout = null
58
+ }
59
+ previous = now
60
+ func.apply(context, args)
61
+ } else if (!timeout) {
62
+ timeout = setTimeout(() => {
63
+ previous = Date.now()
64
+ timeout = null
65
+ func.apply(context, args)
66
+ }, wait)
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * 本地存储工具类
3
+ */
4
+ class StorageUtil {
5
+ private storage: Storage
6
+
7
+ constructor(storage: Storage) {
8
+ this.storage = storage
9
+ }
10
+
11
+ /**
12
+ * 设置存储项
13
+ * @param key - 键名
14
+ * @param value - 值
15
+ * @param expires - 过期时间(毫秒)
16
+ */
17
+ set<T>(key: string, value: T, expires?: number): void {
18
+ const data = {
19
+ value,
20
+ expires: expires ? Date.now() + expires : undefined
21
+ }
22
+ this.storage.setItem(key, JSON.stringify(data))
23
+ }
24
+
25
+ /**
26
+ * 获取存储项
27
+ */
28
+ get<T>(key: string): T | null {
29
+ const item = this.storage.getItem(key)
30
+ if (!item) return null
31
+
32
+ try {
33
+ const data = JSON.parse(item)
34
+
35
+ // 检查是否过期
36
+ if (data.expires && Date.now() > data.expires) {
37
+ this.remove(key)
38
+ return null
39
+ }
40
+
41
+ return data.value as T
42
+ } catch {
43
+ return null
44
+ }
45
+ }
46
+
47
+ /**
48
+ * 移除存储项
49
+ */
50
+ remove(key: string): void {
51
+ this.storage.removeItem(key)
52
+ }
53
+
54
+ /**
55
+ * 清空存储
56
+ */
57
+ clear(): void {
58
+ this.storage.clear()
59
+ }
60
+
61
+ /**
62
+ * 获取所有键
63
+ */
64
+ keys(): string[] {
65
+ const keys: string[] = []
66
+ for (let i = 0; i < this.storage.length; i++) {
67
+ const key = this.storage.key(i)
68
+ if (key) keys.push(key)
69
+ }
70
+ return keys
71
+ }
72
+ }
73
+
74
+ export const localStorageUtil = new StorageUtil(window.localStorage)
75
+ export const sessionStorageUtil = new StorageUtil(window.sessionStorage)
76
+ export const setTheme = (theme: string) => {
77
+ localStorage.setItem('theme', theme);
78
+ sessionStorage.setItem('theme', theme);
79
+ window.document.documentElement.setAttribute('data-theme', theme)
80
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * 首字母大写
3
+ */
4
+ export function capitalize(str: string): string {
5
+ return str.charAt(0).toUpperCase() + str.slice(1)
6
+ }
7
+
8
+ /**
9
+ * 驼峰转连字符
10
+ */
11
+ export function camelToKebab(str: string): string {
12
+ return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
13
+ }
14
+
15
+ /**
16
+ * 连字符转驼峰
17
+ */
18
+ export function kebabToCamel(str: string): string {
19
+ return str.replace(/-([a-z])/g, (_, char) => char.toUpperCase())
20
+ }
21
+
22
+ /**
23
+ * 手机号脱敏
24
+ */
25
+ export function maskPhone(phone: string): string {
26
+ return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
27
+ }
28
+
29
+ /**
30
+ * 身份证号脱敏
31
+ */
32
+ export function maskIdCard(idCard: string): string {
33
+ return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2')
34
+ }
35
+
36
+ /**
37
+ * 银行卡号脱敏
38
+ */
39
+ export function maskBankCard(cardNo: string): string {
40
+ return cardNo.replace(/(\d{4})\d+(\d{4})/, '$1 **** **** $2')
41
+ }
42
+
43
+ /** 邮箱脱敏:t***e@example.com */
44
+ export function maskEmail(email: string): string {
45
+ if (!email || !email.includes('@')) return email
46
+ const [prefix, suffix] = email.split('@')
47
+ const maskedPrefix = prefix.charAt(0) + '***' + prefix.charAt(prefix.length - 1)
48
+ return `${maskedPrefix}@${suffix}`
49
+ }
50
+
51
+ /** 姓名脱敏:张*(2字)/ 张**(3字+) */
52
+ export function maskName(name: string): string {
53
+ if (!name) return name
54
+ if (name.length === 2) return name.charAt(0) + '*'
55
+ return name.charAt(0) + '*'.repeat(name.length - 1)
56
+ }
57
+
58
+ /** 地址脱敏:北京市朝阳区*** */
59
+ export function maskAddress(address: string): string {
60
+ if (!address || address.length <= 6) return address
61
+ return address.substring(0, 6) + '***'
62
+ }
63
+
64
+ /**
65
+ * 截断字符串
66
+ */
67
+ export function truncate(str: string, length: number, suffix = '...'): string {
68
+ if (str.length <= length) return str
69
+ return str.substring(0, length) + suffix
70
+ }
71
+
72
+ /**
73
+ * 移除 HTML 标签
74
+ */
75
+ export function stripHtml(html: string): string {
76
+ return html.replace(/<[^>]*>/g, '')
77
+ }
78
+
79
+ /**
80
+ * 转义 HTML 特殊字符
81
+ */
82
+ export function escapeHtml(str: string): string {
83
+ const escapeMap: Record<string, string> = {
84
+ '&': '&amp;',
85
+ '<': '&lt;',
86
+ '>': '&gt;',
87
+ '"': '&quot;',
88
+ "'": '&#39;'
89
+ }
90
+
91
+ return str.replace(/[&<>"']/g, char => escapeMap[char])
92
+ }
93
+
94
+ /**
95
+ * 解析 URL 参数
96
+ */
97
+ export function parseQueryString(url: string): Record<string, string> {
98
+ const queryString = url.split('?')[1]
99
+ if (!queryString) return {}
100
+
101
+ return queryString.split('&').reduce((params, param) => {
102
+ const [key, value] = param.split('=')
103
+ params[decodeURIComponent(key)] = decodeURIComponent(value || '')
104
+ return params
105
+ }, {} as Record<string, string>)
106
+ }
107
+
108
+ /**
109
+ * 序列化对象为 URL 参数
110
+ */
111
+ export function stringifyQueryString(obj: Record<string, any>): string {
112
+ return Object.entries(obj)
113
+ .filter(([, value]) => value !== undefined && value !== null)
114
+ .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
115
+ .join('&')
116
+ }