@hbdlzy/ui-core 0.1.0 → 0.1.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 (51) hide show
  1. package/README.md +61 -3
  2. package/components.manifest.json +239 -24
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.js +3005 -13
  5. package/dist/style.css +1 -0
  6. package/package.json +5 -3
  7. package/src/components/BaseCard/BaseCard.types.ts +38 -0
  8. package/src/components/BaseCard/BaseCard.vue +250 -0
  9. package/src/components/BaseCard/README.md +164 -0
  10. package/src/components/BaseCard/index.ts +5 -0
  11. package/src/components/BaseEChart/BaseEChart.types.ts +35 -0
  12. package/src/components/BaseEChart/BaseEChart.vue +327 -0
  13. package/src/components/BaseEChart/README.md +136 -0
  14. package/src/components/BaseEChart/index.ts +5 -0
  15. package/src/components/BaseExportButton/BaseExportButton.vue +1 -1
  16. package/src/components/BaseExportButton/README.md +104 -8
  17. package/src/components/BaseTable/BaseTable.types.ts +174 -0
  18. package/src/components/BaseTable/BaseTable.vue +809 -0
  19. package/src/components/BaseTable/README.md +398 -0
  20. package/src/components/BaseTable/index.ts +25 -0
  21. package/src/components/OutlinedCascader/OutlinedCascader.types.ts +28 -0
  22. package/src/components/OutlinedCascader/OutlinedCascader.vue +250 -0
  23. package/src/components/OutlinedCascader/README.md +91 -0
  24. package/src/components/OutlinedCascader/index.ts +10 -0
  25. package/src/components/OutlinedDatePicker/OutlinedDatePicker.types.ts +40 -0
  26. package/src/components/OutlinedDatePicker/OutlinedDatePicker.vue +365 -0
  27. package/src/components/OutlinedDatePicker/README.md +137 -0
  28. package/src/components/OutlinedDatePicker/index.ts +11 -0
  29. package/src/components/OutlinedDateTimePicker/OutlinedDateTimePicker.types.ts +34 -0
  30. package/src/components/OutlinedDateTimePicker/OutlinedDateTimePicker.vue +421 -0
  31. package/src/components/OutlinedDateTimePicker/README.md +88 -0
  32. package/src/components/OutlinedDateTimePicker/index.ts +11 -0
  33. package/src/components/OutlinedInput/OutlinedInput.types.ts +32 -0
  34. package/src/components/OutlinedInput/OutlinedInput.vue +307 -0
  35. package/src/components/OutlinedInput/README.md +154 -0
  36. package/src/components/OutlinedInput/index.ts +10 -0
  37. package/src/components/OutlinedSelect/OutlinedSelect.types.ts +48 -0
  38. package/src/components/OutlinedSelect/OutlinedSelect.vue +359 -0
  39. package/src/components/OutlinedSelect/README.md +166 -0
  40. package/src/components/OutlinedSelect/index.ts +12 -0
  41. package/src/components/OutlinedTimePicker/OutlinedTimePicker.types.ts +36 -0
  42. package/src/components/OutlinedTimePicker/OutlinedTimePicker.vue +303 -0
  43. package/src/components/OutlinedTimePicker/README.md +93 -0
  44. package/src/components/OutlinedTimePicker/index.ts +10 -0
  45. package/src/components/OutlinedTreeSelect/OutlinedTreeSelect.types.ts +56 -0
  46. package/src/components/OutlinedTreeSelect/OutlinedTreeSelect.vue +354 -0
  47. package/src/components/OutlinedTreeSelect/README.md +107 -0
  48. package/src/components/OutlinedTreeSelect/index.ts +12 -0
  49. package/src/echarts/index.ts +12 -0
  50. package/src/excel/exportExcel.ts +36 -10
  51. package/src/index.ts +29 -0
@@ -0,0 +1,359 @@
1
+ <template>
2
+ <div
3
+ class="outlined-select"
4
+ :class="[containerClasses, rootClass]"
5
+ :style="[containerStyle, rootStyle]"
6
+ >
7
+ <el-select
8
+ ref="selectRef"
9
+ v-bind="selectAttrs"
10
+ class="outlined-select__control"
11
+ :model-value="selectValue"
12
+ :placeholder="placeholder"
13
+ :disabled="disabled"
14
+ :multiple="multiple"
15
+ :filterable="filterable"
16
+ :clearable="clearable"
17
+ :collapse-tags="collapseTags"
18
+ :collapse-tags-tooltip="collapseTagsTooltip"
19
+ :max-collapse-tags="maxCollapseTags"
20
+ :style="selectStyle"
21
+ @update:model-value="handleModelValueUpdate"
22
+ @focus="handleFocus"
23
+ @blur="handleBlur"
24
+ @change="handleChange"
25
+ @visible-change="handleVisibleChange"
26
+ @clear="handleClear"
27
+ >
28
+ <slot v-if="hasCustomOptionsSlot" />
29
+
30
+ <template v-else>
31
+ <el-option
32
+ v-for="(item, index) in options"
33
+ :key="createOptionKey(item, index)"
34
+ :label="resolveOptionLabel(item)"
35
+ :value="resolveOptionValue(item)"
36
+ :disabled="Boolean(item.disabled)"
37
+ />
38
+ </template>
39
+
40
+ <template #empty>
41
+ <slot name="empty" />
42
+ </template>
43
+
44
+ <template #header>
45
+ <slot name="header" />
46
+ </template>
47
+
48
+ <template #footer>
49
+ <slot name="footer" />
50
+ </template>
51
+
52
+ <template #tag="slotProps">
53
+ <slot
54
+ name="tag"
55
+ v-bind="slotProps"
56
+ />
57
+ </template>
58
+ </el-select>
59
+
60
+ <div
61
+ v-if="floatingLabel"
62
+ class="outlined-select__label"
63
+ :class="labelClasses"
64
+ >
65
+ {{ floatingLabel }}
66
+ </div>
67
+ </div>
68
+ </template>
69
+
70
+ <script setup lang="ts">
71
+ import { computed, ref, useAttrs, useSlots, watch } from 'vue'
72
+ import type {
73
+ OutlinedSelectCssValue,
74
+ OutlinedSelectExpose,
75
+ OutlinedSelectOption,
76
+ OutlinedSelectOptionValue,
77
+ OutlinedSelectProps,
78
+ OutlinedSelectValue
79
+ } from './OutlinedSelect.types'
80
+
81
+ defineOptions({
82
+ name: 'OutlinedSelect',
83
+ inheritAttrs: false
84
+ })
85
+
86
+ const props = withDefaults(defineProps<OutlinedSelectProps>(), {
87
+ value: undefined,
88
+ options: () => [],
89
+ placeholder: '',
90
+ label: '',
91
+ disabled: false,
92
+ typeInput: '',
93
+ showPassword: false,
94
+ collapseTags: false,
95
+ collapseTagsTooltip: true,
96
+ filterable: true,
97
+ clearable: true,
98
+ multiple: false,
99
+ inputHeight: 40,
100
+ isBorder: false,
101
+ keyValue: 'value',
102
+ labelValue: 'label',
103
+ noCheck: false,
104
+ maxCollapseTags: 5,
105
+ marginBottom: 20,
106
+ paddingTop: 20
107
+ })
108
+
109
+ const emit = defineEmits<{
110
+ (event: 'input', value: OutlinedSelectValue | ''): void
111
+ (event: 'update:value', value: OutlinedSelectValue | ''): void
112
+ (event: 'change', value: OutlinedSelectValue | ''): void
113
+ (event: 'focus', value: FocusEvent): void
114
+ (event: 'blur', value: FocusEvent): void
115
+ (event: 'visible-change', value: boolean): void
116
+ (event: 'clear'): void
117
+ }>()
118
+
119
+ const attrs = useAttrs()
120
+ const slots = useSlots()
121
+ const selectRef = ref<any>(null)
122
+ const selectValue = ref<OutlinedSelectValue | ''>(normalizeSelectValue(props.value, props.multiple))
123
+ const focused = ref(false)
124
+ const blurred = ref(false)
125
+ const visible = ref(false)
126
+
127
+ watch(
128
+ () => [props.value, props.multiple] as const,
129
+ ([value, multiple]) => {
130
+ selectValue.value = normalizeSelectValue(value, multiple)
131
+ },
132
+ { immediate: true }
133
+ )
134
+
135
+ const rootClass = computed(() => attrs.class)
136
+ const rootStyle = computed(() => attrs.style)
137
+
138
+ const selectAttrs = computed(() => {
139
+ const { class: _class, style: _style, ...rest } = attrs
140
+ return rest
141
+ })
142
+
143
+ const containerClasses = computed(() => ({
144
+ 'outlined-select--bordered': props.isBorder
145
+ }))
146
+
147
+ const containerStyle = computed(() => ({
148
+ marginBottom: toCssValue(props.marginBottom),
149
+ paddingTop: toCssValue(props.paddingTop)
150
+ }))
151
+
152
+ const selectStyle = computed(() => ({
153
+ minHeight: `${props.inputHeight}px`,
154
+ height: props.multiple ? 'auto' : `${props.inputHeight}px`
155
+ }))
156
+
157
+ const floatingLabel = computed(() => props.label || props.placeholder || '')
158
+ const hasCustomOptionsSlot = computed(() => Boolean(slots.default))
159
+
160
+ const hasContent = computed(() => {
161
+ if (props.multiple) {
162
+ return Array.isArray(selectValue.value) && selectValue.value.length > 0
163
+ }
164
+
165
+ return hasFilledValue(selectValue.value)
166
+ })
167
+
168
+ const shouldFloat = computed(() => (props.noCheck ? focused.value : focused.value || hasContent.value))
169
+
170
+ const labelClasses = computed(() => ({
171
+ 'outlined-select__label--floating': shouldFloat.value,
172
+ 'outlined-select__label--placeholder': !shouldFloat.value,
173
+ 'outlined-select__label--filled': !props.noCheck && hasContent.value && !focused.value
174
+ }))
175
+
176
+ function handleModelValueUpdate(value: OutlinedSelectValue) {
177
+ const normalizedValue = normalizeSelectValue(value, props.multiple)
178
+ selectValue.value = normalizedValue
179
+ emitModelValue(normalizedValue)
180
+ }
181
+
182
+ function handleChange(value: OutlinedSelectValue) {
183
+ const normalizedValue = normalizeSelectValue(value, props.multiple)
184
+ selectValue.value = normalizedValue
185
+ emit('change', normalizedValue)
186
+ }
187
+
188
+ function handleFocus(event: FocusEvent) {
189
+ blurred.value = false
190
+ focused.value = true
191
+ emit('focus', event)
192
+ }
193
+
194
+ function handleBlur(event: FocusEvent) {
195
+ blurred.value = true
196
+
197
+ if (!visible.value) {
198
+ focused.value = false
199
+ emitModelValue(selectValue.value)
200
+ }
201
+
202
+ emit('blur', event)
203
+ }
204
+
205
+ function handleVisibleChange(nextVisible: boolean) {
206
+ visible.value = nextVisible
207
+
208
+ if (!nextVisible && blurred.value) {
209
+ focused.value = false
210
+ }
211
+
212
+ emit('visible-change', nextVisible)
213
+ }
214
+
215
+ function handleClear() {
216
+ emit('clear')
217
+ }
218
+
219
+ function emitModelValue(value: OutlinedSelectValue | '') {
220
+ emit('input', value)
221
+ emit('update:value', value)
222
+ }
223
+
224
+ function normalizeSelectValue(value: OutlinedSelectValue | undefined, multiple: boolean): OutlinedSelectValue | '' {
225
+ if (multiple) {
226
+ if (Array.isArray(value)) {
227
+ return value
228
+ }
229
+
230
+ if (value === undefined || value === null || value === '') {
231
+ return []
232
+ }
233
+
234
+ return [value]
235
+ }
236
+
237
+ return value ?? ''
238
+ }
239
+
240
+ function resolveOptionLabel(option: OutlinedSelectOption) {
241
+ const label = option[props.labelValue]
242
+ return label === undefined || label === null ? '' : String(label)
243
+ }
244
+
245
+ function resolveOptionValue(option: OutlinedSelectOption): OutlinedSelectOptionValue {
246
+ return (option[props.keyValue] as OutlinedSelectOptionValue) ?? null
247
+ }
248
+
249
+ function createOptionKey(option: OutlinedSelectOption, index: number) {
250
+ const value = resolveOptionValue(option)
251
+ return `${index}-${typeof value === 'object' ? JSON.stringify(value) : String(value)}`
252
+ }
253
+
254
+ function hasFilledValue(value: unknown) {
255
+ return value !== undefined && value !== null && String(value) !== ''
256
+ }
257
+
258
+ function toCssValue(value: OutlinedSelectCssValue | undefined) {
259
+ if (typeof value === 'number') {
260
+ return `${value}px`
261
+ }
262
+
263
+ return value
264
+ }
265
+
266
+ defineExpose<OutlinedSelectExpose>({
267
+ focus: () => {
268
+ selectRef.value?.focus?.()
269
+ },
270
+ blur: () => {
271
+ selectRef.value?.blur?.()
272
+ },
273
+ clear: () => {
274
+ selectRef.value?.blur?.()
275
+
276
+ if (props.multiple) {
277
+ selectValue.value = []
278
+ emitModelValue([])
279
+ emit('change', [])
280
+ emit('clear')
281
+ return
282
+ }
283
+
284
+ selectValue.value = ''
285
+ emitModelValue('')
286
+ emit('change', '')
287
+ emit('clear')
288
+ },
289
+ toggleMenu: () => {
290
+ selectRef.value?.toggleMenu?.()
291
+ },
292
+ getSelectRef: () => selectRef.value
293
+ })
294
+ </script>
295
+
296
+ <style scoped>
297
+ .outlined-select {
298
+ position: relative;
299
+ }
300
+
301
+ .outlined-select__label {
302
+ position: absolute;
303
+ left: -2px;
304
+ padding: 2px 5px;
305
+ border-radius: 2px;
306
+ background-color: #ffffff;
307
+ font-size: 12px;
308
+ line-height: 1;
309
+ transition: color 200ms cubic-bezier(0, 0, 0.2, 1), transform 200ms cubic-bezier(0, 0, 0.2, 1);
310
+ pointer-events: none;
311
+ }
312
+
313
+ .outlined-select__label--floating {
314
+ top: 18px;
315
+ z-index: 1;
316
+ opacity: 1;
317
+ transform: translate(14px, -6px) scale(1);
318
+ color: var(--el-color-primary);
319
+ }
320
+
321
+ .outlined-select__label--placeholder {
322
+ top: 50%;
323
+ left: 1px;
324
+ z-index: -1;
325
+ opacity: 0;
326
+ transform: translate(14px, 6px) scale(1);
327
+ }
328
+
329
+ .outlined-select__label--filled {
330
+ color: var(--el-text-color-regular);
331
+ }
332
+
333
+ :deep(.outlined-select__control.el-select) {
334
+ width: 100%;
335
+ }
336
+
337
+ :deep(.outlined-select__control .el-select__wrapper) {
338
+ min-height: inherit;
339
+ height: inherit;
340
+ }
341
+
342
+ :deep(.outlined-select__control .el-select__tags) {
343
+ padding-top: 4px;
344
+ }
345
+
346
+ :deep(.outlined-select__control .el-select__selection) {
347
+ min-height: inherit;
348
+ }
349
+
350
+ :deep(.outlined-select--bordered .el-select__wrapper) {
351
+ box-shadow: none;
352
+ border-radius: 0;
353
+ border-bottom: 1px solid #cccccc;
354
+ }
355
+
356
+ :deep(.outlined-select--bordered .el-select__wrapper.is-focused) {
357
+ border-bottom-color: var(--el-color-primary);
358
+ }
359
+ </style>
@@ -0,0 +1,166 @@
1
+ # OutlinedSelect
2
+
3
+ `OutlinedSelect` 用来统一项目里带浮动标签样式的下拉选择框,避免页面层重复写下拉框高度、标签悬浮、值映射和焦点控制逻辑。
4
+
5
+ ## 解决的问题
6
+
7
+ - 统一浮动标签下拉框样式
8
+ - 统一 `v-model:value`、`input`、`update:value` 和 `change` 事件约定
9
+ - 统一 `options / keyValue / labelValue` 配置式下拉渲染
10
+ - 自动处理弹层展开时的焦点态和浮动标签状态
11
+ - 通过组件实例直接调用 `focus`、`blur`、`clear`、`toggleMenu`
12
+
13
+ ## 适用场景
14
+
15
+ - 页面需要和 `OutlinedInput / OutlinedDatePicker` 保持一致视觉语言
16
+ - 表单页里有大量重复的基础下拉框
17
+ - 希望页面只传 `options` 和映射字段,不想再重复写 `el-option`
18
+ - 需要兼容旧项目里 `key-value / label-value / noCheck` 这类写法
19
+
20
+ ## 不适用场景
21
+
22
+ - 页面需要完全自定义选择器结构
23
+ - 单页中需要强业务联动的复杂远程搜索选择器
24
+ - 不是浮动标签风格的下拉框
25
+
26
+ ## 基础用法
27
+
28
+ ```vue
29
+ <template>
30
+ <OutlinedSelect
31
+ v-model:value="form.mode"
32
+ placeholder="参与方式"
33
+ :is-border="true"
34
+ :options="modeOptions"
35
+ />
36
+ </template>
37
+
38
+ <script setup lang="ts">
39
+ import { reactive } from 'vue'
40
+ import { OutlinedSelect } from '@hbdlzy/ui-core'
41
+
42
+ const form = reactive({
43
+ mode: ''
44
+ })
45
+
46
+ const modeOptions = [
47
+ { label: '现货', value: 'spot' },
48
+ { label: '调频', value: 'fm' }
49
+ ]
50
+ </script>
51
+ ```
52
+
53
+ ## 自定义字段映射示例
54
+
55
+ ```vue
56
+ <template>
57
+ <OutlinedSelect
58
+ v-model:value="form.nodeName"
59
+ placeholder="所属节点"
60
+ :options="nodeOptions"
61
+ key-value="nodeName"
62
+ label-value="nodeName"
63
+ :is-border="true"
64
+ />
65
+ </template>
66
+
67
+ <script setup lang="ts">
68
+ import { reactive } from 'vue'
69
+ import { OutlinedSelect } from '@hbdlzy/ui-core'
70
+
71
+ const form = reactive({
72
+ nodeName: ''
73
+ })
74
+
75
+ const nodeOptions = [
76
+ { nodeName: '华北节点' },
77
+ { nodeName: '华东节点' }
78
+ ]
79
+ </script>
80
+ ```
81
+
82
+ ## 多选示例
83
+
84
+ ```vue
85
+ <template>
86
+ <OutlinedSelect
87
+ v-model:value="selectedTags"
88
+ placeholder="标签"
89
+ :options="tagOptions"
90
+ multiple
91
+ collapse-tags
92
+ :is-border="true"
93
+ />
94
+ </template>
95
+
96
+ <script setup lang="ts">
97
+ import { ref } from 'vue'
98
+ import { OutlinedSelect } from '@hbdlzy/ui-core'
99
+
100
+ const selectedTags = ref<string[]>([])
101
+
102
+ const tagOptions = [
103
+ { label: '削峰填谷', value: 'peak' },
104
+ { label: '调频服务', value: 'fm' },
105
+ { label: '收益分析', value: 'income' }
106
+ ]
107
+ </script>
108
+ ```
109
+
110
+ ## Props
111
+
112
+ - `value`: 当前值,推荐通过 `v-model:value` 使用
113
+ - `options`: 配置式选项列表,默认 `[]`
114
+ - `placeholder`: 占位文案,同时默认作为浮动标签文案
115
+ - `label`: 自定义浮动标签文案,未传时回退到 `placeholder`
116
+ - `disabled`: 是否禁用
117
+ - `collapseTags`: 多选时是否折叠标签
118
+ - `collapseTagsTooltip`: 折叠标签时是否显示 tooltip
119
+ - `filterable`: 是否可搜索,默认 `true`
120
+ - `clearable`: 是否可清空,默认 `true`
121
+ - `multiple`: 是否多选
122
+ - `inputHeight`: 控件高度基准,默认 `40`
123
+ - `isBorder`: 是否启用底部边框样式
124
+ - `keyValue`: 选项值字段名,默认 `value`
125
+ - `labelValue`: 选项显示字段名,默认 `label`
126
+ - `noCheck`: 是否只根据焦点控制浮动标签
127
+ - `maxCollapseTags`: 折叠标签时最大显示数量,默认 `5`
128
+ - `marginBottom`: 外层容器底部间距,默认 `20`
129
+ - `paddingTop`: 外层容器顶部留白,默认 `20`
130
+
131
+ ## Events
132
+
133
+ - `input`: 值变化时触发
134
+ - `update:value`: `v-model:value` 对应更新事件
135
+ - `change`: 用户确认变更时触发
136
+ - `focus`: 聚焦时触发
137
+ - `blur`: 失焦时触发
138
+ - `visible-change`: 下拉面板展开状态变化时触发
139
+ - `clear`: 点击清空按钮时触发
140
+
141
+ ## Slots
142
+
143
+ - `default`: 完全自定义选项内容
144
+ - `empty`: 空状态插槽
145
+ - `header`: 下拉头部插槽
146
+ - `footer`: 下拉底部插槽
147
+ - `tag`: 多选标签插槽
148
+
149
+ ## Expose
150
+
151
+ - `focus`: 手动聚焦选择器
152
+ - `blur`: 手动失焦选择器
153
+ - `clear`: 手动清空当前值
154
+ - `toggleMenu`: 手动展开或关闭下拉面板
155
+ - `getSelectRef`: 获取底层 `el-select` 实例
156
+
157
+ ## 透传规则
158
+
159
+ - 未被组件显式声明的属性,会自动透传给内部 `el-select`
160
+ - 适合继续传 `teleported`、`remote`、`remote-method`、`reserve-keyword` 等 Element Plus 原生能力
161
+
162
+ ## 推荐约定
163
+
164
+ - 新页面优先使用 `v-model:value`
165
+ - 常规下拉优先传 `options`
166
+ - 只有需要复杂远程搜索或强定制选项时,才考虑使用 `default` 插槽完全接管
@@ -0,0 +1,12 @@
1
+ import OutlinedSelect from './OutlinedSelect.vue'
2
+
3
+ export default OutlinedSelect
4
+
5
+ export type {
6
+ OutlinedSelectCssValue,
7
+ OutlinedSelectExpose,
8
+ OutlinedSelectOption,
9
+ OutlinedSelectOptionValue,
10
+ OutlinedSelectProps,
11
+ OutlinedSelectValue
12
+ } from './OutlinedSelect.types'
@@ -0,0 +1,36 @@
1
+ export type OutlinedTimePickerCssValue = string | number
2
+
3
+ export type OutlinedTimePickerValue =
4
+ | string
5
+ | number
6
+ | Date
7
+ | Array<string | number | Date>
8
+ | null
9
+
10
+ export interface OutlinedTimePickerProps {
11
+ value?: OutlinedTimePickerValue
12
+ placeholder?: string
13
+ label?: string
14
+ disabled?: boolean
15
+ clearable?: boolean
16
+ isRange?: boolean
17
+ startPlaceholder?: string
18
+ endPlaceholder?: string
19
+ rangeSeparator?: string
20
+ format?: string
21
+ valueFormat?: string
22
+ arrowControl?: boolean
23
+ inputHeight?: number
24
+ isBorder?: boolean
25
+ marginBottom?: OutlinedTimePickerCssValue
26
+ paddingTop?: OutlinedTimePickerCssValue
27
+ }
28
+
29
+ export interface OutlinedTimePickerExpose {
30
+ focus: () => void
31
+ blur: () => void
32
+ handleOpen: () => void
33
+ handleClose: () => void
34
+ clear: () => void
35
+ getPickerRef: () => unknown | null
36
+ }