@hbdlzy/ui-core 0.1.0 → 0.1.1
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/README.md +25 -2
- package/components.manifest.json +185 -2
- package/dist/index.cjs +1 -1
- package/dist/index.js +1140 -12
- package/dist/style.css +1 -0
- package/package.json +4 -3
- package/src/components/BaseCard/BaseCard.types.ts +38 -0
- package/src/components/BaseCard/BaseCard.vue +250 -0
- package/src/components/BaseCard/README.md +164 -0
- package/src/components/BaseCard/index.ts +5 -0
- package/src/components/BaseEChart/BaseEChart.types.ts +35 -0
- package/src/components/BaseEChart/BaseEChart.vue +327 -0
- package/src/components/BaseEChart/README.md +136 -0
- package/src/components/BaseEChart/index.ts +5 -0
- package/src/components/BaseExportButton/BaseExportButton.vue +1 -1
- package/src/components/BaseExportButton/README.md +104 -8
- package/src/components/BaseTable/BaseTable.types.ts +174 -0
- package/src/components/BaseTable/BaseTable.vue +809 -0
- package/src/components/BaseTable/README.md +398 -0
- package/src/components/BaseTable/index.ts +25 -0
- package/src/echarts/index.ts +12 -0
- package/src/excel/exportExcel.ts +36 -10
- package/src/index.ts +15 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="containerRef" class="base-echart">
|
|
3
|
+
<div ref="chartRef" class="base-echart__inner"></div>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import { nextTick, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'
|
|
9
|
+
import { echarts } from '../../echarts'
|
|
10
|
+
import type {
|
|
11
|
+
EChartInitOptions,
|
|
12
|
+
EChartInstance,
|
|
13
|
+
EChartLoadingOptions,
|
|
14
|
+
EChartOption,
|
|
15
|
+
EChartResizeOptions,
|
|
16
|
+
EChartSetOptionOptions
|
|
17
|
+
} from '../../echarts'
|
|
18
|
+
import type { BaseEChartExpose, BaseEChartProps } from './BaseEChart.types'
|
|
19
|
+
|
|
20
|
+
const props = withDefaults(defineProps<BaseEChartProps>(), {
|
|
21
|
+
option: () => ({}),
|
|
22
|
+
initOptions: () => ({ renderer: 'canvas' } as EChartInitOptions),
|
|
23
|
+
setOptionOptions: () => ({ notMerge: false, lazyUpdate: true } as EChartSetOptionOptions),
|
|
24
|
+
loading: false,
|
|
25
|
+
autoResize: true,
|
|
26
|
+
useDefaultColors: true,
|
|
27
|
+
colorStorageKey: 'echartsColor',
|
|
28
|
+
defaultColors: () => [],
|
|
29
|
+
group: undefined
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const emit = defineEmits<{
|
|
33
|
+
(event: 'ready', chart: EChartInstance): void
|
|
34
|
+
}>()
|
|
35
|
+
|
|
36
|
+
const containerRef = ref<HTMLElement | null>(null)
|
|
37
|
+
const chartRef = ref<HTMLElement | null>(null)
|
|
38
|
+
const chartInstance = shallowRef<EChartInstance | null>(null)
|
|
39
|
+
|
|
40
|
+
let resizeObserver: ResizeObserver | null = null
|
|
41
|
+
let resizeFrameId: number | null = null
|
|
42
|
+
let pendingResizeOptions: EChartResizeOptions | undefined
|
|
43
|
+
|
|
44
|
+
const cancelResizeFrame = () => {
|
|
45
|
+
if (resizeFrameId !== null) {
|
|
46
|
+
cancelAnimationFrame(resizeFrameId)
|
|
47
|
+
resizeFrameId = null
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const getDefaultColors = () => {
|
|
52
|
+
if (!props.useDefaultColors || typeof window === 'undefined') {
|
|
53
|
+
return props.defaultColors
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const raw = window.sessionStorage.getItem(props.colorStorageKey)
|
|
58
|
+
const colors = raw ? (JSON.parse(raw) as string[]) : []
|
|
59
|
+
return colors.length > 0 ? colors : props.defaultColors
|
|
60
|
+
} catch {
|
|
61
|
+
return props.defaultColors
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const normalizeOption = (option: EChartOption = {}) => {
|
|
66
|
+
if (option.color) {
|
|
67
|
+
return option
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const colors = getDefaultColors()
|
|
71
|
+
if (!colors || colors.length === 0) {
|
|
72
|
+
return option
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
...option,
|
|
77
|
+
color: colors
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const createChart = () => {
|
|
82
|
+
if (!chartRef.value) {
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const existingChart = echarts.getInstanceByDom(chartRef.value)
|
|
87
|
+
existingChart?.dispose()
|
|
88
|
+
|
|
89
|
+
const instance = echarts.init(chartRef.value, props.theme, props.initOptions)
|
|
90
|
+
if (props.group) {
|
|
91
|
+
instance.group = props.group
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
chartInstance.value = instance
|
|
95
|
+
emit('ready', instance)
|
|
96
|
+
return instance
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const getInstance = () => chartInstance.value
|
|
100
|
+
|
|
101
|
+
const ensureChart = () => chartInstance.value ?? createChart()
|
|
102
|
+
|
|
103
|
+
const setOption = (
|
|
104
|
+
option: EChartOption,
|
|
105
|
+
setOptionOptions: boolean | EChartSetOptionOptions = props.setOptionOptions
|
|
106
|
+
) => {
|
|
107
|
+
const chart = ensureChart()
|
|
108
|
+
if (!chart) {
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const nextOption = normalizeOption(option)
|
|
113
|
+
if (typeof setOptionOptions === 'boolean') {
|
|
114
|
+
chart.setOption(nextOption, setOptionOptions)
|
|
115
|
+
} else {
|
|
116
|
+
chart.setOption(nextOption, setOptionOptions)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return chart
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const showLoading = (type = 'default', loadingOptions?: EChartLoadingOptions) => {
|
|
123
|
+
const chart = ensureChart()
|
|
124
|
+
if (!chart) {
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
chart.showLoading(type, loadingOptions)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const hideLoading = () => {
|
|
132
|
+
chartInstance.value?.hideLoading()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const syncLoading = () => {
|
|
136
|
+
if (props.loading) {
|
|
137
|
+
showLoading('default', props.loadingOptions)
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
hideLoading()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const resize = (resizeOptions?: EChartResizeOptions) => {
|
|
145
|
+
pendingResizeOptions = resizeOptions
|
|
146
|
+
cancelResizeFrame()
|
|
147
|
+
|
|
148
|
+
resizeFrameId = requestAnimationFrame(() => {
|
|
149
|
+
chartInstance.value?.resize(pendingResizeOptions)
|
|
150
|
+
pendingResizeOptions = undefined
|
|
151
|
+
resizeFrameId = null
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const handleWindowResize = () => {
|
|
156
|
+
resize()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const stopAutoResize = () => {
|
|
160
|
+
resizeObserver?.disconnect()
|
|
161
|
+
resizeObserver = null
|
|
162
|
+
window.removeEventListener('resize', handleWindowResize)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const startAutoResize = () => {
|
|
166
|
+
stopAutoResize()
|
|
167
|
+
|
|
168
|
+
if (!props.autoResize || !containerRef.value) {
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
resizeObserver = new ResizeObserver(() => {
|
|
173
|
+
resize()
|
|
174
|
+
})
|
|
175
|
+
resizeObserver.observe(containerRef.value)
|
|
176
|
+
window.addEventListener('resize', handleWindowResize)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const clear = () => {
|
|
180
|
+
chartInstance.value?.clear()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const disposeChart = () => {
|
|
184
|
+
chartInstance.value?.dispose()
|
|
185
|
+
chartInstance.value = null
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const dispose = () => {
|
|
189
|
+
disposeChart()
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const dispatchAction = (payload: Record<string, unknown>) => {
|
|
193
|
+
chartInstance.value?.dispatchAction(payload as never)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const on = (eventName: string, handler: (...args: any[]) => void, context?: unknown) => {
|
|
197
|
+
const chart = ensureChart()
|
|
198
|
+
chart?.on(eventName, handler, context)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const off = (eventName?: string, handler?: (...args: any[]) => void) => {
|
|
202
|
+
if (!chartInstance.value) {
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!eventName) {
|
|
207
|
+
chartInstance.value.off()
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (handler) {
|
|
212
|
+
chartInstance.value.off(eventName, handler)
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
chartInstance.value.off(eventName)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const reinitChart = async () => {
|
|
220
|
+
disposeChart()
|
|
221
|
+
await nextTick()
|
|
222
|
+
createChart()
|
|
223
|
+
setOption(props.option)
|
|
224
|
+
syncLoading()
|
|
225
|
+
resize()
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
watch(
|
|
229
|
+
() => props.option,
|
|
230
|
+
option => {
|
|
231
|
+
setOption(option)
|
|
232
|
+
},
|
|
233
|
+
{ deep: true }
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
watch(
|
|
237
|
+
() => props.loading,
|
|
238
|
+
() => {
|
|
239
|
+
syncLoading()
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
watch(
|
|
244
|
+
() => props.loadingOptions,
|
|
245
|
+
() => {
|
|
246
|
+
if (props.loading) {
|
|
247
|
+
syncLoading()
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
{ deep: true }
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
watch(
|
|
254
|
+
() => props.group,
|
|
255
|
+
group => {
|
|
256
|
+
if (!chartInstance.value) {
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
chartInstance.value.group = group ?? ''
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
watch(
|
|
265
|
+
() => props.autoResize,
|
|
266
|
+
async enabled => {
|
|
267
|
+
stopAutoResize()
|
|
268
|
+
if (enabled) {
|
|
269
|
+
await nextTick()
|
|
270
|
+
startAutoResize()
|
|
271
|
+
resize()
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
watch(
|
|
277
|
+
() => props.theme,
|
|
278
|
+
() => {
|
|
279
|
+
reinitChart()
|
|
280
|
+
},
|
|
281
|
+
{ deep: true }
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
watch(
|
|
285
|
+
() => props.initOptions,
|
|
286
|
+
() => {
|
|
287
|
+
reinitChart()
|
|
288
|
+
},
|
|
289
|
+
{ deep: true }
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
onMounted(async () => {
|
|
293
|
+
await nextTick()
|
|
294
|
+
createChart()
|
|
295
|
+
setOption(props.option)
|
|
296
|
+
syncLoading()
|
|
297
|
+
startAutoResize()
|
|
298
|
+
resize()
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
onUnmounted(() => {
|
|
302
|
+
stopAutoResize()
|
|
303
|
+
cancelResizeFrame()
|
|
304
|
+
disposeChart()
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
defineExpose<BaseEChartExpose>({
|
|
308
|
+
getInstance,
|
|
309
|
+
setOption,
|
|
310
|
+
resize,
|
|
311
|
+
dispatchAction,
|
|
312
|
+
showLoading,
|
|
313
|
+
hideLoading,
|
|
314
|
+
clear,
|
|
315
|
+
dispose,
|
|
316
|
+
on,
|
|
317
|
+
off
|
|
318
|
+
})
|
|
319
|
+
</script>
|
|
320
|
+
|
|
321
|
+
<style scoped>
|
|
322
|
+
.base-echart,
|
|
323
|
+
.base-echart__inner {
|
|
324
|
+
width: 100%;
|
|
325
|
+
height: 100%;
|
|
326
|
+
}
|
|
327
|
+
</style>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# BaseEChart
|
|
2
|
+
|
|
3
|
+
`BaseEChart` 用来统一封装项目里重复出现的 ECharts 初始化、更新、尺寸监听和实例销毁逻辑。
|
|
4
|
+
|
|
5
|
+
## 解决的问题
|
|
6
|
+
|
|
7
|
+
- 页面只传 `option` 就能渲染图表
|
|
8
|
+
- 自动处理 `init`、`setOption`、`resize`、`dispose`
|
|
9
|
+
- 容器尺寸变化时自动 `resize`
|
|
10
|
+
- 不传 `color` 时自动读取 `sessionStorage.echartsColor`
|
|
11
|
+
- 需要手动控制时,可以直接通过组件实例调用 ECharts 方法
|
|
12
|
+
|
|
13
|
+
## 适用场景
|
|
14
|
+
|
|
15
|
+
- 大部分常规图表页面
|
|
16
|
+
- 希望页面层只维护 `option`
|
|
17
|
+
- 需要统一收敛图表的生命周期代码
|
|
18
|
+
- 需要在页面层偶尔调用 `resize`、`dispatchAction`、`showLoading`
|
|
19
|
+
|
|
20
|
+
## 不适用场景
|
|
21
|
+
|
|
22
|
+
- 需要完全接管图表 DOM 生命周期
|
|
23
|
+
- 需要自定义复杂容器交互且不适合组件封装
|
|
24
|
+
- 单个页面里要做强定制图表引擎适配
|
|
25
|
+
|
|
26
|
+
## 基础用法
|
|
27
|
+
|
|
28
|
+
父容器必须有明确高度。
|
|
29
|
+
|
|
30
|
+
```vue
|
|
31
|
+
<template>
|
|
32
|
+
<div style="height: 320px">
|
|
33
|
+
<BaseEChart :option="option" />
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { BaseEChart, echarts, type EChartOption } from '@hbdlzy/ui-core'
|
|
39
|
+
|
|
40
|
+
const option: EChartOption = {
|
|
41
|
+
tooltip: {
|
|
42
|
+
trigger: 'axis'
|
|
43
|
+
},
|
|
44
|
+
xAxis: {
|
|
45
|
+
type: 'category',
|
|
46
|
+
data: ['01', '02', '03']
|
|
47
|
+
},
|
|
48
|
+
yAxis: {
|
|
49
|
+
type: 'value'
|
|
50
|
+
},
|
|
51
|
+
series: [
|
|
52
|
+
{
|
|
53
|
+
type: 'line',
|
|
54
|
+
smooth: true,
|
|
55
|
+
data: [12, 18, 15],
|
|
56
|
+
areaStyle: {
|
|
57
|
+
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
58
|
+
{ offset: 0, color: 'rgba(24, 144, 255, 0.35)' },
|
|
59
|
+
{ offset: 1, color: 'rgba(24, 144, 255, 0)' }
|
|
60
|
+
])
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 直接调用实例方法
|
|
69
|
+
|
|
70
|
+
```vue
|
|
71
|
+
<script setup lang="ts">
|
|
72
|
+
import { ref } from 'vue'
|
|
73
|
+
import { BaseEChart, type BaseEChartExpose } from '@hbdlzy/ui-core'
|
|
74
|
+
|
|
75
|
+
const chartRef = ref<BaseEChartExpose | null>(null)
|
|
76
|
+
|
|
77
|
+
const refreshChart = () => {
|
|
78
|
+
chartRef.value?.resize()
|
|
79
|
+
chartRef.value?.dispatchAction({
|
|
80
|
+
type: 'hideTip'
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<template>
|
|
86
|
+
<BaseEChart ref="chartRef" :option="option" />
|
|
87
|
+
</template>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 直接调用 echarts 原生能力
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { echarts } from '@hbdlzy/ui-core'
|
|
94
|
+
|
|
95
|
+
const gradient = new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
96
|
+
{ offset: 0, color: '#2f88ff' },
|
|
97
|
+
{ offset: 1, color: 'rgba(47, 136, 255, 0)' }
|
|
98
|
+
])
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Props
|
|
102
|
+
|
|
103
|
+
- `option`: 图表配置,直接透传给 `setOption`
|
|
104
|
+
- `theme`: ECharts 主题
|
|
105
|
+
- `initOptions`: `echarts.init` 的第三个参数
|
|
106
|
+
- `setOptionOptions`: `setOption` 的第二个参数
|
|
107
|
+
- `loading`: 是否显示 loading
|
|
108
|
+
- `loadingOptions`: loading 配置
|
|
109
|
+
- `autoResize`: 是否自动监听尺寸变化并执行 `resize`
|
|
110
|
+
- `useDefaultColors`: 当 `option.color` 未传时,是否自动读取默认配色
|
|
111
|
+
- `colorStorageKey`: 默认配色的 `sessionStorage` key,默认 `echartsColor`
|
|
112
|
+
- `defaultColors`: 默认配色兜底值
|
|
113
|
+
- `group`: 图表联动分组
|
|
114
|
+
|
|
115
|
+
## Events
|
|
116
|
+
|
|
117
|
+
- `ready`: 图表实例初始化完成时触发,返回 ECharts 实例
|
|
118
|
+
|
|
119
|
+
## Expose
|
|
120
|
+
|
|
121
|
+
- `getInstance`: 获取当前 ECharts 实例
|
|
122
|
+
- `setOption`: 手动设置 option
|
|
123
|
+
- `resize`: 手动触发 resize
|
|
124
|
+
- `dispatchAction`: 调用原生 `dispatchAction`
|
|
125
|
+
- `showLoading`: 显示 loading
|
|
126
|
+
- `hideLoading`: 隐藏 loading
|
|
127
|
+
- `clear`: 清空图表
|
|
128
|
+
- `dispose`: 销毁图表实例
|
|
129
|
+
- `on`: 绑定 ECharts 事件
|
|
130
|
+
- `off`: 解绑 ECharts 事件
|
|
131
|
+
|
|
132
|
+
## 推荐约定
|
|
133
|
+
|
|
134
|
+
- 页面层只维护 `option`,不要重复写 `echarts.init`
|
|
135
|
+
- 默认走 `BaseEChart + option`,只有确实需要时再通过 `ref` 调暴露方法
|
|
136
|
+
- 渐变色、图形工具优先从 `@hbdlzy/ui-core` 导出的 `echarts` 使用
|
|
@@ -140,7 +140,7 @@ async function handleExport() {
|
|
|
140
140
|
|
|
141
141
|
try {
|
|
142
142
|
if (exportMode.value === 'excel' && props.excelOptions) {
|
|
143
|
-
exportExcel(props.excelOptions)
|
|
143
|
+
await exportExcel(props.excelOptions)
|
|
144
144
|
|
|
145
145
|
if (props.autoMessage) {
|
|
146
146
|
ElMessage.success(props.successMessage)
|
|
@@ -1,25 +1,121 @@
|
|
|
1
1
|
# BaseExportButton
|
|
2
2
|
|
|
3
|
-
`BaseExportButton`
|
|
3
|
+
`BaseExportButton` 用来统一项目中的导出入口,避免每个页面重复写按钮状态、导出前校验、成功失败提示和文件下载逻辑。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 解决的问题
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
7
|
+
- 统一前端 Excel 导出按钮交互
|
|
8
|
+
- 统一后端 `blob` 文件下载按钮交互
|
|
9
|
+
- 自动处理 loading 状态
|
|
10
|
+
- 自动处理成功和失败消息提示
|
|
11
|
+
- 将导出前校验、导出成功回调收敛成统一接口
|
|
9
12
|
|
|
10
|
-
##
|
|
13
|
+
## 适用场景
|
|
14
|
+
|
|
15
|
+
- 页面需要导出表格数据为 Excel
|
|
16
|
+
- 页面需要调用后端接口导出文件
|
|
17
|
+
- 希望多个项目中的导出按钮交互保持一致
|
|
18
|
+
|
|
19
|
+
## 不适用场景
|
|
20
|
+
|
|
21
|
+
- 页面需要复杂的导出配置弹窗
|
|
22
|
+
- 导出前需要强业务表单流程编排
|
|
23
|
+
- 不是按钮触发型的导出入口
|
|
24
|
+
|
|
25
|
+
## 支持模式
|
|
26
|
+
|
|
27
|
+
- `excelOptions`: 纯前端 Excel 导出
|
|
28
|
+
- `requestHandler`: 调接口获取 `blob` 或二进制响应后下载
|
|
29
|
+
|
|
30
|
+
这两个模式二选一,不能同时传。
|
|
31
|
+
|
|
32
|
+
## 基础用法
|
|
11
33
|
|
|
12
34
|
```vue
|
|
35
|
+
<template>
|
|
36
|
+
<BaseExportButton :excel-options="excelOptions" />
|
|
37
|
+
</template>
|
|
38
|
+
|
|
13
39
|
<script setup lang="ts">
|
|
14
|
-
import { BaseExportButton } from '@hbdlzy/ui-core'
|
|
40
|
+
import { BaseExportButton, type ExcelExportOptions } from '@hbdlzy/ui-core'
|
|
41
|
+
|
|
42
|
+
interface UserRow {
|
|
43
|
+
name: string
|
|
44
|
+
department: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const excelOptions: ExcelExportOptions<UserRow> = {
|
|
48
|
+
fileName: '用户列表',
|
|
49
|
+
columns: [
|
|
50
|
+
{ label: '姓名', key: 'name' },
|
|
51
|
+
{ label: '部门', key: 'department' }
|
|
52
|
+
],
|
|
53
|
+
data: [
|
|
54
|
+
{ name: '张三', department: '运维' },
|
|
55
|
+
{ name: '李四', department: '调度' }
|
|
56
|
+
]
|
|
57
|
+
}
|
|
15
58
|
</script>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 后端导出示例
|
|
16
62
|
|
|
63
|
+
```vue
|
|
17
64
|
<template>
|
|
18
|
-
<BaseExportButton
|
|
65
|
+
<BaseExportButton
|
|
66
|
+
file-name="运行报表.xlsx"
|
|
67
|
+
:request-handler="requestHandler"
|
|
68
|
+
:before-export="beforeExport"
|
|
69
|
+
/>
|
|
19
70
|
</template>
|
|
71
|
+
|
|
72
|
+
<script setup lang="ts">
|
|
73
|
+
import { BaseExportButton } from '@hbdlzy/ui-core'
|
|
74
|
+
|
|
75
|
+
const beforeExport = async () => {
|
|
76
|
+
return true
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const requestHandler = async () => {
|
|
80
|
+
const response = await api.exportReport()
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
data: response.data,
|
|
84
|
+
headers: response.headers
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
</script>
|
|
20
88
|
```
|
|
21
89
|
|
|
90
|
+
## Props
|
|
91
|
+
|
|
92
|
+
- `label`: 按钮文案,默认 `导出`
|
|
93
|
+
- `type`: Element Plus 按钮类型,默认 `primary`
|
|
94
|
+
- `size`: 按钮尺寸,默认 `default`
|
|
95
|
+
- `plain`: 是否朴素按钮
|
|
96
|
+
- `link`: 是否链接按钮
|
|
97
|
+
- `text`: 是否文字按钮
|
|
98
|
+
- `disabled`: 是否禁用
|
|
99
|
+
- `fileName`: 后端下载时的兜底文件名
|
|
100
|
+
- `excelOptions`: 前端 Excel 导出配置
|
|
101
|
+
- `requestHandler`: 后端导出请求函数
|
|
102
|
+
- `beforeExport`: 导出前钩子,返回 `false` 时中断导出
|
|
103
|
+
- `successMessage`: 成功提示文案
|
|
104
|
+
- `errorMessage`: 失败提示文案
|
|
105
|
+
- `autoMessage`: 是否自动弹出成功失败提示
|
|
106
|
+
|
|
107
|
+
## Events
|
|
108
|
+
|
|
109
|
+
- `start`: 点击并通过校验后开始导出
|
|
110
|
+
- `success`: 导出成功,返回 `{ mode, fileName }`
|
|
111
|
+
- `error`: 导出失败
|
|
112
|
+
|
|
22
113
|
## 约束
|
|
23
114
|
|
|
24
115
|
- `excelOptions` 和 `requestHandler` 二选一
|
|
25
|
-
- 组件只负责触发导出,不负责业务数据查询
|
|
116
|
+
- 组件只负责触发导出,不负责业务数据查询
|
|
117
|
+
- 如果使用前端导出,建议优先复用 `exportExcel`
|
|
118
|
+
|
|
119
|
+
## 相关能力
|
|
120
|
+
|
|
121
|
+
- 工具函数:`@hbdlzy/ui-core` 中的 `exportExcel`
|