@jzt-packages/components 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.
Files changed (145) hide show
  1. package/package.json +68 -0
  2. package/src/JztBackTop/index.vue +255 -0
  3. package/src/JztButtonList/index.vue +88 -0
  4. package/src/JztChart/index.vue +95 -0
  5. package/src/JztCharts/index.vue +317 -0
  6. package/src/JztClassTabs/index.vue +156 -0
  7. package/src/JztDateSelect/dateSelect.vue +186 -0
  8. package/src/JztDateSelect/dateType.vue +54 -0
  9. package/src/JztDateSelect/index.ts +135 -0
  10. package/src/JztDateSelect/interface/index.ts +13 -0
  11. package/src/JztDialog/index.vue +249 -0
  12. package/src/JztEllipsisTooltip/index.vue +61 -0
  13. package/src/JztEmpty/index.vue +45 -0
  14. package/src/JztErrorPage/403.vue +30 -0
  15. package/src/JztErrorPage/404.vue +19 -0
  16. package/src/JztErrorPage/500.vue +18 -0
  17. package/src/JztErrorPage/assets/401.png +0 -0
  18. package/src/JztErrorPage/assets/403.png +0 -0
  19. package/src/JztErrorPage/assets/404.png +0 -0
  20. package/src/JztErrorPage/assets/500.png +0 -0
  21. package/src/JztErrorPage/index.scss +35 -0
  22. package/src/JztErrorPage/index.vue +35 -0
  23. package/src/JztFilePreview/components/pdfViewer.vue +221 -0
  24. package/src/JztFilePreview/hooks/useImageMethod.ts +256 -0
  25. package/src/JztFilePreview/index.scss +171 -0
  26. package/src/JztFilePreview/index.vue +68 -0
  27. package/src/JztFilePreview/interface/index.ts +18 -0
  28. package/src/JztFilePreview/previewFile.vue +371 -0
  29. package/src/JztFormGrid/README.md +520 -0
  30. package/src/JztFormGrid/components/formItem.vue +209 -0
  31. package/src/JztFormGrid/components/formItemValue.vue +384 -0
  32. package/src/JztFormGrid/components/showDetailForm.vue +172 -0
  33. package/src/JztFormGrid/index.scss +60 -0
  34. package/src/JztFormGrid/index.vue +513 -0
  35. package/src/JztFormGrid/interface/index.ts +106 -0
  36. package/src/JztGrid/components/GridItem.vue +68 -0
  37. package/src/JztGrid/index.vue +179 -0
  38. package/src/JztGrid/interface/index.ts +6 -0
  39. package/src/JztImportExcel/assets/delete.png +0 -0
  40. package/src/JztImportExcel/index.scss +46 -0
  41. package/src/JztImportExcel/index.vue +430 -0
  42. package/src/JztImportExcel/interface/index.ts +25 -0
  43. package/src/JztLabelTitle/index.vue +65 -0
  44. package/src/JztLeftRightMode/components/CollapseButton.vue +80 -0
  45. package/src/JztLeftRightMode/components/LeftCard.vue +203 -0
  46. package/src/JztLeftRightMode/components/LeftLayout.vue +173 -0
  47. package/src/JztLeftRightMode/components/RightHeader.vue +186 -0
  48. package/src/JztLeftRightMode/components/RightLayout.vue +235 -0
  49. package/src/JztLeftRightMode/components/RightTableHeader.vue +43 -0
  50. package/src/JztLeftRightMode/hooks/useCollapse.ts +17 -0
  51. package/src/JztLeftRightMode/hooks/useDefaultProps.ts +19 -0
  52. package/src/JztLeftRightMode/hooks/useLeftLayout.ts +201 -0
  53. package/src/JztLeftRightMode/hooks/useMode.ts +20 -0
  54. package/src/JztLeftRightMode/hooks/usePrevNext.ts +60 -0
  55. package/src/JztLeftRightMode/hooks/useRightLayout.ts +215 -0
  56. package/src/JztLeftRightMode/hooks/useSlots.ts +15 -0
  57. package/src/JztLeftRightMode/index.ts +3 -0
  58. package/src/JztLeftRightMode/index.vue +494 -0
  59. package/src/JztLeftRightMode/types/index.ts +457 -0
  60. package/src/JztLoading/fullScreen.ts +45 -0
  61. package/src/JztLoading/index.scss +67 -0
  62. package/src/JztLoading/index.vue +18 -0
  63. package/src/JztLogin/components/LoginFooter.vue +17 -0
  64. package/src/JztLogin/components/LoginForm.vue +99 -0
  65. package/src/JztLogin/hooks/useLogin.ts +186 -0
  66. package/src/JztLogin/index.scss +142 -0
  67. package/src/JztLogin/index.vue +31 -0
  68. package/src/JztLogin/interface/index.ts +47 -0
  69. package/src/JztNumericalRange/index.vue +81 -0
  70. package/src/JztPageCard/comm/datePicker.vue +151 -0
  71. package/src/JztPageCard/comm/details.vue +60 -0
  72. package/src/JztPageCard/comm/export.vue +24 -0
  73. package/src/JztPageCard/comm/tabs.vue +94 -0
  74. package/src/JztPageCard/comm/tooltip.vue +31 -0
  75. package/src/JztPageCard/index.vue +287 -0
  76. package/src/JztPagination/index.vue +70 -0
  77. package/src/JztProductInfo/components/imagePreview.vue +275 -0
  78. package/src/JztProductInfo/components/qxUnique.vue +101 -0
  79. package/src/JztProductInfo/components/records.vue +265 -0
  80. package/src/JztProductInfo/hooks/useParams.ts +143 -0
  81. package/src/JztProductInfo/hooks/useQxUnique.tsx +466 -0
  82. package/src/JztProductInfo/images/defaultProduct.png +0 -0
  83. package/src/JztProductInfo/index.ts +116 -0
  84. package/src/JztProductInfo/index.vue +108 -0
  85. package/src/JztProductInfo/interface/index.ts +15 -0
  86. package/src/JztQueryDetailTable/index.scss +100 -0
  87. package/src/JztQueryDetailTable/index.vue +400 -0
  88. package/src/JztQueryDetailTable/interface/index.ts +10 -0
  89. package/src/JztQueryTable/QueryTable /345/212/237/350/203/275.md" +1580 -0
  90. package/src/JztQueryTable/README.md +567 -0
  91. package/src/JztQueryTable/components/ColSetting.vue +67 -0
  92. package/src/JztQueryTable/components/ColumnsSetting.vue +404 -0
  93. package/src/JztQueryTable/components/ColumnsSetting1.vue +220 -0
  94. package/src/JztQueryTable/components/DeployToAccountLevelSetting.vue +351 -0
  95. package/src/JztQueryTable/components/Pagination.vue +54 -0
  96. package/src/JztQueryTable/components/TableColumn.vue +109 -0
  97. package/src/JztQueryTable/const.ts +1 -0
  98. package/src/JztQueryTable/hooks/useQueryTable.ts +194 -0
  99. package/src/JztQueryTable/hooks/useSelection.ts +47 -0
  100. package/src/JztQueryTable/hooks/useTableSetting.ts +197 -0
  101. package/src/JztQueryTable/hooks/useTemplate.ts +127 -0
  102. package/src/JztQueryTable/index.scss +91 -0
  103. package/src/JztQueryTable/index.vue +1445 -0
  104. package/src/JztQueryTable/interface/index.ts +185 -0
  105. package/src/JztRegionSelect/index.vue +134 -0
  106. package/src/JztSearchForm/components/SearchFormItem.vue +473 -0
  107. package/src/JztSearchForm/index.vue +530 -0
  108. package/src/JztSearchForm/interface/index.ts +100 -0
  109. package/src/JztSelectFilter/index.scss +63 -0
  110. package/src/JztSelectFilter/index.vue +110 -0
  111. package/src/JztSelectTable/index.vue +257 -0
  112. package/src/JztTable/index.scss +72 -0
  113. package/src/JztTable/index.vue +353 -0
  114. package/src/JztTable/interface/index.ts +1 -0
  115. package/src/JztTime/comm/agencySelect.vue +112 -0
  116. package/src/JztTime/comm/collapseRow.vue +132 -0
  117. package/src/JztTime/comm/dateSelect.vue +292 -0
  118. package/src/JztTime/comm/deptSelect.vue +193 -0
  119. package/src/JztTime/comm/typeSelect.vue +97 -0
  120. package/src/JztTime/index.ts +216 -0
  121. package/src/JztTime/index.vue +303 -0
  122. package/src/JztTime/interface/index.ts +23 -0
  123. package/src/JztTreeFilter/index.scss +44 -0
  124. package/src/JztTreeFilter/index.vue +177 -0
  125. package/src/JztUploadFile/interface/index.ts +21 -0
  126. package/src/JztUploadFile/multiple.scss +215 -0
  127. package/src/JztUploadFile/multiple.vue +318 -0
  128. package/src/JztUploadFile/single.scss +226 -0
  129. package/src/JztUploadFile/single.vue +274 -0
  130. package/src/JztUploadImg/Img.vue +294 -0
  131. package/src/JztUploadImg/Imgs.vue +411 -0
  132. package/src/JztUploadImg/index.scss +138 -0
  133. package/src/JztUploadImg/interface/index.ts +22 -0
  134. package/src/SelectIcon/index.scss +39 -0
  135. package/src/SelectIcon/index.vue +106 -0
  136. package/src/SvgIcon/index.vue +22 -0
  137. package/src/hooks/useAuthButtons.ts +58 -0
  138. package/src/hooks/useFormByUserType.ts +90 -0
  139. package/src/hooks/useTableEvents.ts +30 -0
  140. package/src/hooks/useUploadFileHook.ts +262 -0
  141. package/src/index.ts +91 -0
  142. package/src/typings/global.d.ts +101 -0
  143. package/src/utils/index.ts +107 -0
  144. package/src/utils/tree.ts +57 -0
  145. package/tsconfig.json +45 -0
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@jzt-packages/components",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "src/index.ts",
6
+ "keywords": [],
7
+ "author": "lixiaoli",
8
+ "license": "ISC",
9
+ "dependencies": {
10
+ "@jzt-spd/styles": "workspace:^",
11
+ "@jzt-spd/utils": "workspace:^",
12
+ "@types/echarts": "catalog:",
13
+ "@vueuse/core": "^14.1.0",
14
+ "echarts": "catalog:",
15
+ "jsencrypt": "^3.3.2",
16
+ "lodash-es": "catalog:",
17
+ "pinia": "catalog:",
18
+ "sortablejs": "catalog:",
19
+ "vue": "catalog:",
20
+ "vue-pdf-embed": "catalog:",
21
+ "dayjs": "catalog:"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "import": "./src/index.ts"
26
+ },
27
+ "./jztGrid": {
28
+ "import": "./src/JztGrid/interface/index.ts",
29
+ "types": "./src/JztGrid/interface/index.ts"
30
+ },
31
+ "./JztTable": {
32
+ "import": "./src/JztTable/interface/index.ts",
33
+ "types": "./src/JztTable/interface/index.ts"
34
+ },
35
+ "./JztSearchForm": {
36
+ "import": "./src/JztSearchForm/interface/index.ts",
37
+ "types": "./src/JztSearchForm/interface/index.ts"
38
+ },
39
+ "./JztQueryTable": {
40
+ "import": "./src/JztQueryTable/interface/index.ts",
41
+ "types": "./src/JztQueryTable/interface/index.ts"
42
+ },
43
+ "./JztFormGrid": {
44
+ "import": "./src/JztFormGrid/interface/index.ts",
45
+ "types": "./src/JztFormGrid/interface/index.ts"
46
+ },
47
+ "./JztQueryDetailTable": {
48
+ "import": "./src/JztQueryDetailTable/interface/index.ts",
49
+ "types": "./src/JztQueryDetailTable/interface/index.ts"
50
+ },
51
+ "./JztFilePreview": {
52
+ "import": "./src/JztFilePreview/interface/index.ts",
53
+ "types": "./src/JztFilePreview/interface/index.ts"
54
+ },
55
+ "./JztProductInfo": {
56
+ "import": "./src/JztProductInfo/interface/index.ts",
57
+ "types": "./src/JztProductInfo/interface/index.ts"
58
+ },
59
+ "./JztDialog": {
60
+ "import": "./src/JztDialog/interface/index.ts",
61
+ "types": "./src/JztDialog/interface/index.ts"
62
+ },
63
+ "./JztImportExcel": {
64
+ "import": "./src/JztImportExcel/interface/index.ts",
65
+ "types": "./src/JztImportExcel/interface/index.ts"
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,255 @@
1
+ <script setup lang="ts" name="JztBackTop">
2
+ import { JztSvgIcon } from '@jzt-spd/components'
3
+ import { useDraggable } from '@vueuse/core'
4
+ import { computed, nextTick, onMounted, onUnmounted, ref, useTemplateRef } from 'vue'
5
+
6
+ // ==================== 类型定义 ====================
7
+ /**
8
+ * 回到顶部组件 Props 定义
9
+ */
10
+ export type Props = {
11
+ /**
12
+ * 组件大小
13
+ * @default 20
14
+ */
15
+ size?: number
16
+ /**
17
+ * 定位方式
18
+ * @default fixed
19
+ */
20
+ position?: 'absolute' | 'fixed'
21
+ /**
22
+ * 容器选择器
23
+ * @default body
24
+ */
25
+ container?: string
26
+ /**
27
+ * 滚动容器选择器
28
+ * @default body
29
+ */
30
+ scrollContainer?: string
31
+ /**
32
+ * 显示阈值(滚动超过此值显示组件)
33
+ * @default 0
34
+ */
35
+ threshold?: number
36
+ /**
37
+ * 初始X坐标
38
+ * @default 0
39
+ */
40
+ x?: number
41
+ /**
42
+ * 初始Y坐标
43
+ * @default 0
44
+ */
45
+ y?: number
46
+ /**
47
+ * 层级
48
+ * @default 1999
49
+ */
50
+ zIndex?: number
51
+ }
52
+
53
+ // ==================== Props 与默认值 ====================
54
+ const props = withDefaults(defineProps<Props>(), {
55
+ size: 20,
56
+ position: 'fixed',
57
+ container: 'body',
58
+ scrollContainer: 'body',
59
+ threshold: 0,
60
+ x: 0,
61
+ y: 0,
62
+ zIndex: 1999
63
+ })
64
+
65
+ // ==================== 响应式状态 ====================
66
+ /** 点击阈值(用于判断是拖拽还是点击) */
67
+ const CLICK_THRESHOLD = 5
68
+ let startX = 0
69
+ let startY = 0
70
+ let isClick = false
71
+
72
+ /** 组件模板引用 */
73
+ const backTopRef = useTemplateRef<HTMLElement>('backTopRef')
74
+ /** 是否显示组件 */
75
+ const isVisible = ref(false)
76
+ /** 容器元素引用 */
77
+ const containerRef = ref<HTMLElement | null>(null)
78
+ /** 滚动容器元素引用 */
79
+ const scrollContainerRef = ref<HTMLElement | null>(null)
80
+ /** 拖拽实例 */
81
+ const draggable = ref<ReturnType<typeof useDraggable>>()
82
+
83
+ // ==================== 计算属性 ====================
84
+ /**
85
+ * 组件样式
86
+ */
87
+ const style = computed(() => {
88
+ const { size, position, zIndex } = props
89
+ const { x, y } = draggable.value ?? {}
90
+ return {
91
+ width: `${size}px`,
92
+ height: `${size}px`,
93
+ left: `${x}px`,
94
+ top: `${y}px`,
95
+ position,
96
+ zIndex
97
+ }
98
+ })
99
+
100
+ // ==================== 核心方法 ====================
101
+ /**
102
+ * 滚动事件处理(计算是否显示组件)
103
+ */
104
+ const scroll = () => {
105
+ const scrollEl = scrollContainerRef.value
106
+ if (scrollEl) {
107
+ const scrollTop = scrollEl === document.body ? window.scrollY : scrollEl.scrollTop
108
+ isVisible.value = scrollTop > props.threshold
109
+ }
110
+ }
111
+
112
+ /**
113
+ * 滚动到顶部
114
+ */
115
+ const scrollToTop = () => {
116
+ const scrollEl = scrollContainerRef.value
117
+ if (scrollEl) {
118
+ if (scrollEl === document.body) {
119
+ window.scrollTo({ top: 0, behavior: 'smooth' })
120
+ } else {
121
+ scrollEl.scrollTo({ top: 0, behavior: 'smooth' })
122
+ }
123
+ }
124
+ }
125
+
126
+ /**
127
+ * 添加滚动监听
128
+ */
129
+ const addScrollListener = (el: HTMLElement | null) => {
130
+ if (el === document.body) {
131
+ window.addEventListener('scroll', scroll)
132
+ } else {
133
+ el?.addEventListener('scroll', scroll)
134
+ }
135
+ }
136
+
137
+ /**
138
+ * 移除滚动监听
139
+ */
140
+ const removeScrollListener = (el: HTMLElement | null) => {
141
+ if (el === document.body) {
142
+ window.removeEventListener('scroll', scroll)
143
+ } else {
144
+ el?.removeEventListener('scroll', scroll)
145
+ }
146
+ }
147
+
148
+ /**
149
+ * 初始化容器
150
+ */
151
+ const setupContainer = () => {
152
+ containerRef.value = document.querySelector(props.container) as HTMLElement | null
153
+ }
154
+
155
+ /**
156
+ * 初始化拖拽功能
157
+ */
158
+ const setupDraggable = () => {
159
+ const { size } = props
160
+ const { clientWidth = 0, clientHeight = 0 } = containerRef.value ?? {}
161
+ draggable.value = useDraggable(backTopRef, {
162
+ initialValue: () => {
163
+ let { x, y } = props
164
+ if (x < 0) {
165
+ x = clientWidth + x - size
166
+ }
167
+ if (y < 0) {
168
+ y = clientHeight + y - size
169
+ }
170
+ return { x, y }
171
+ },
172
+ containerElement: () => {
173
+ return containerRef.value
174
+ },
175
+ onStart: position => {
176
+ scrollContainerRef.value?.style?.setProperty('overflow', 'hidden')
177
+ isClick = true
178
+ startX = position.x
179
+ startY = position.y
180
+ },
181
+ onEnd: () => {
182
+ backTopRef.value?.style.setProperty('cursor', 'pointer')
183
+ scrollContainerRef.value?.style?.setProperty('overflow', 'auto')
184
+ if (isClick) {
185
+ scrollToTop()
186
+ }
187
+ },
188
+ onMove: position => {
189
+ backTopRef.value?.style.setProperty('cursor', 'move')
190
+ if (position.x > clientWidth - size) {
191
+ position.x = clientWidth - size
192
+ }
193
+ if (position.y > clientHeight - size) {
194
+ position.y = clientHeight - size
195
+ }
196
+ const distanceX = Math.abs(position.x - startX)
197
+ const distanceY = Math.abs(position.y - startY)
198
+ if (distanceX > CLICK_THRESHOLD || distanceY > CLICK_THRESHOLD) {
199
+ isClick = false
200
+ }
201
+ }
202
+ })
203
+ }
204
+
205
+ /**
206
+ * 初始化滚动容器
207
+ */
208
+ const setupScrollContainer = () => {
209
+ scrollContainerRef.value = document.querySelector(props.scrollContainer) as HTMLElement | null
210
+ if (scrollContainerRef.value) {
211
+ addScrollListener(scrollContainerRef.value)
212
+ } else {
213
+ const observer = new MutationObserver(() => {
214
+ const target = document.querySelector(props.scrollContainer)
215
+ if (target) {
216
+ scrollContainerRef.value = target as HTMLElement
217
+ addScrollListener(scrollContainerRef.value)
218
+ observer.disconnect()
219
+ }
220
+ })
221
+ observer.observe(containerRef.value as Node, { childList: true })
222
+ }
223
+ }
224
+
225
+ // ==================== 生命周期 ====================
226
+ onMounted(async () => {
227
+ await nextTick()
228
+ setupContainer()
229
+ setupDraggable()
230
+ setupScrollContainer()
231
+ })
232
+
233
+ onUnmounted(() => {
234
+ removeScrollListener(scrollContainerRef.value)
235
+ })
236
+ </script>
237
+
238
+ <template>
239
+ <transition name="el-fade-in-linear">
240
+ <div v-show="isVisible" ref="backTopRef" class="back-top" :style="style">
241
+ <slot>
242
+ <jzt-svg-icon :icon-style="{ width: '100%', height: '100%', color: '#ffffff' }" name="back-top" />
243
+ </slot>
244
+ </div>
245
+ </transition>
246
+ </template>
247
+
248
+ <style lang="scss" scoped>
249
+ .back-top {
250
+ cursor: pointer;
251
+ border-radius: 50%;
252
+ box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
253
+ user-select: none;
254
+ }
255
+ </style>
@@ -0,0 +1,88 @@
1
+ <template>
2
+ <!-- <div class="button-list"> -->
3
+ <template v-for="(item, index) in buttons" :key="index">
4
+ <component v-if="item?.render" :is="item.render" />
5
+ <el-badge
6
+ v-else-if="item?.badgeProps?.show && hasPermission(item.hasPerm) && (item.hideFun ? !item.hideFun() : true)"
7
+ class="item"
8
+ v-bind="item.badgeProps"
9
+ :offset="[-8, 5]"
10
+ >
11
+ <el-button
12
+ class="table-top-btn"
13
+ :text="false"
14
+ :type="item.type || 'default'"
15
+ :loading="item.loading"
16
+ :icon="item.icon || ''"
17
+ :plain="item.plain ?? true"
18
+ :disabled="item.disabledFn && item.disabledFn()"
19
+ @click="handleButtonClick(item)"
20
+ >
21
+ {{ item.text }}
22
+ </el-button>
23
+ </el-badge>
24
+ <!-- </div> -->
25
+ <el-button
26
+ v-else-if="hasPermission(item.hasPerm) && (item.hideFun ? !item.hideFun() : true)"
27
+ class="table-top-btn"
28
+ :text="false"
29
+ :type="item.type || 'default'"
30
+ :loading="item.loading"
31
+ :icon="item.icon || ''"
32
+ :plain="item.plain ?? true"
33
+ :disabled="item.disabledFn && item.disabledFn()"
34
+ @click="handleButtonClick(item)"
35
+ >
36
+ {{ item.text }}
37
+ </el-button>
38
+ </template>
39
+ <slot name="tableHeader"></slot>
40
+ <!-- </div> -->
41
+ </template>
42
+
43
+ <script setup lang="ts" name="ProTable">
44
+ import { computed } from 'vue'
45
+ // import { hasPermission } from '../hooks/useAuthButtons'
46
+ import { hasPermission } from '@jzt-spd/utils'
47
+ import { TopButtonProps } from '../JztQueryTable/interface/index'
48
+
49
+ export interface ProTableProps {
50
+ buttonList?: TopButtonProps[] // 按钮组list数据
51
+ }
52
+
53
+ // 接受父组件参数,配置默认值
54
+ const props = withDefaults(defineProps<ProTableProps>(), {
55
+ buttonList: () => []
56
+ })
57
+
58
+ const buttons = computed(() => props.buttonList || [])
59
+
60
+ const emit = defineEmits<{
61
+ onClick: [any]
62
+ }>()
63
+
64
+ // 是否显示表格操作按钮
65
+ // const hasPermission = (item: any) => {
66
+ // const { BUTTONS } = useAuthButtons()
67
+ // if (!item.hasPerm) return true
68
+ // return BUTTONS.value[item.hasPerm]
69
+ // }
70
+ // 按钮点击
71
+ const handleButtonClick = (item: TopButtonProps) => {
72
+ if (item.fun) {
73
+ emit('onClick', item.fun)
74
+ }
75
+ }
76
+ // 暴露给父组件的参数和方法 (外部需要什么,都可以从这里暴露出去)
77
+ defineExpose({})
78
+ </script>
79
+ <style lang="scss" scoped>
80
+ :deep(.header-button-lf) {
81
+ .el-button {
82
+ margin: 4px 8px 4px 0;
83
+ }
84
+ }
85
+ .el-button {
86
+ margin: 4px 8px 4px 0;
87
+ }
88
+ </style>
@@ -0,0 +1,95 @@
1
+ <template>
2
+ <div ref="chartRef" :style="{ width, height }"></div>
3
+ </template>
4
+
5
+ <script lang="ts">
6
+ import { defineComponent, ref, onMounted, onBeforeUnmount, watch } from 'vue';
7
+ import * as echarts from 'echarts';
8
+ import type { EChartsOption, ECharts } from 'echarts';
9
+
10
+ export default defineComponent({
11
+ name: 'JztChart',
12
+ props: {
13
+ options: {
14
+ type: Object as () => EChartsOption,
15
+ required: true
16
+ },
17
+ width: {
18
+ type: String,
19
+ default: '100%'
20
+ },
21
+ height: {
22
+ type: String,
23
+ default: '400px'
24
+ },
25
+ theme: {
26
+ type: [String, Object],
27
+ default: ''
28
+ },
29
+ initOptions: {
30
+ type: Object,
31
+ default: () => ({})
32
+ }
33
+ },
34
+ setup(props) {
35
+ const chartRef = ref<HTMLElement>();
36
+ let chartInstance: ECharts | null = null;
37
+ let resizeObserver: ResizeObserver | null = null;
38
+
39
+ const initChart = () => {
40
+ if (!chartRef.value) return;
41
+
42
+ chartInstance = echarts.init(chartRef.value, props.theme, props.initOptions);
43
+ chartInstance.setOption(props.options);
44
+ };
45
+
46
+ // 添加防抖函数
47
+ const debounceResize = debounce(() => {
48
+ chartInstance?.resize();
49
+ }, 200);
50
+
51
+ // 防抖函数实现
52
+ function debounce(fn: Function, delay: number) {
53
+ let timer: number | null = null;
54
+ return function() {
55
+ if (timer) clearTimeout(timer);
56
+ timer = setTimeout(fn, delay);
57
+ };
58
+ }
59
+
60
+ onMounted(() => {
61
+ initChart();
62
+
63
+ // 方式1:使用 window resize 事件(添加防抖)
64
+ window.addEventListener('resize', debounceResize);
65
+
66
+ // 方式2:使用 ResizeObserver(更推荐)
67
+ if (chartRef.value) {
68
+ resizeObserver = new ResizeObserver(debounceResize);
69
+ resizeObserver.observe(chartRef.value);
70
+ }
71
+ });
72
+
73
+ onBeforeUnmount(() => {
74
+ // 清理两种监听方式
75
+ window.removeEventListener('resize', debounceResize);
76
+ if (resizeObserver) {
77
+ resizeObserver.disconnect();
78
+ resizeObserver = null;
79
+ }
80
+ chartInstance?.dispose();
81
+ chartInstance = null;
82
+ });
83
+
84
+ watch(
85
+ () => props.options,
86
+ (newOptions) => {
87
+ chartInstance?.setOption(newOptions);
88
+ },
89
+ { deep: true }
90
+ );
91
+
92
+ return { chartRef };
93
+ }
94
+ });
95
+ </script>