@hbdlzy/ui-core 0.1.1 → 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 (35) hide show
  1. package/README.md +37 -2
  2. package/components.manifest.json +195 -163
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.js +1877 -13
  5. package/dist/style.css +1 -1
  6. package/package.json +2 -1
  7. package/src/components/OutlinedCascader/OutlinedCascader.types.ts +28 -0
  8. package/src/components/OutlinedCascader/OutlinedCascader.vue +250 -0
  9. package/src/components/OutlinedCascader/README.md +91 -0
  10. package/src/components/OutlinedCascader/index.ts +10 -0
  11. package/src/components/OutlinedDatePicker/OutlinedDatePicker.types.ts +40 -0
  12. package/src/components/OutlinedDatePicker/OutlinedDatePicker.vue +365 -0
  13. package/src/components/OutlinedDatePicker/README.md +137 -0
  14. package/src/components/OutlinedDatePicker/index.ts +11 -0
  15. package/src/components/OutlinedDateTimePicker/OutlinedDateTimePicker.types.ts +34 -0
  16. package/src/components/OutlinedDateTimePicker/OutlinedDateTimePicker.vue +421 -0
  17. package/src/components/OutlinedDateTimePicker/README.md +88 -0
  18. package/src/components/OutlinedDateTimePicker/index.ts +11 -0
  19. package/src/components/OutlinedInput/OutlinedInput.types.ts +32 -0
  20. package/src/components/OutlinedInput/OutlinedInput.vue +307 -0
  21. package/src/components/OutlinedInput/README.md +154 -0
  22. package/src/components/OutlinedInput/index.ts +10 -0
  23. package/src/components/OutlinedSelect/OutlinedSelect.types.ts +48 -0
  24. package/src/components/OutlinedSelect/OutlinedSelect.vue +359 -0
  25. package/src/components/OutlinedSelect/README.md +166 -0
  26. package/src/components/OutlinedSelect/index.ts +12 -0
  27. package/src/components/OutlinedTimePicker/OutlinedTimePicker.types.ts +36 -0
  28. package/src/components/OutlinedTimePicker/OutlinedTimePicker.vue +303 -0
  29. package/src/components/OutlinedTimePicker/README.md +93 -0
  30. package/src/components/OutlinedTimePicker/index.ts +10 -0
  31. package/src/components/OutlinedTreeSelect/OutlinedTreeSelect.types.ts +56 -0
  32. package/src/components/OutlinedTreeSelect/OutlinedTreeSelect.vue +354 -0
  33. package/src/components/OutlinedTreeSelect/README.md +107 -0
  34. package/src/components/OutlinedTreeSelect/index.ts +12 -0
  35. package/src/index.ts +14 -0
@@ -0,0 +1,365 @@
1
+ <template>
2
+ <div
3
+ class="outlined-date-picker"
4
+ :class="[containerClasses, rootClass]"
5
+ :style="[containerStyle, rootStyle]"
6
+ >
7
+ <el-date-picker
8
+ ref="pickerRef"
9
+ v-bind="pickerAttrs"
10
+ class="outlined-date-picker__control"
11
+ :model-value="pickerValue"
12
+ :type="typeDate"
13
+ :format="format"
14
+ :value-format="resolvedValueFormat"
15
+ :placeholder="placeholder"
16
+ :disabled="disabled"
17
+ :style="pickerStyle"
18
+ @update:model-value="handleModelValueUpdate"
19
+ @focus="handleFocus"
20
+ @blur="handleBlur"
21
+ @change="handleChange"
22
+ @visible-change="handleVisibleChange"
23
+ />
24
+
25
+ <div
26
+ v-if="floatingLabel"
27
+ class="outlined-date-picker__label"
28
+ :class="labelClasses"
29
+ >
30
+ {{ floatingLabel }}
31
+ </div>
32
+ </div>
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import { computed, ref, useAttrs, watch } from 'vue'
37
+ import type {
38
+ OutlinedDatePickerCssValue,
39
+ OutlinedDatePickerDisabledDate,
40
+ OutlinedDatePickerExpose,
41
+ OutlinedDatePickerProps,
42
+ OutlinedDatePickerValue
43
+ } from './OutlinedDatePicker.types'
44
+
45
+ defineOptions({
46
+ name: 'OutlinedDatePicker',
47
+ inheritAttrs: false
48
+ })
49
+
50
+ const props = withDefaults(defineProps<OutlinedDatePickerProps>(), {
51
+ value: '',
52
+ placeholder: '',
53
+ label: '',
54
+ disabled: false,
55
+ timeValue: false,
56
+ typeDate: 'date',
57
+ format: 'YYYY-MM-DD',
58
+ valueFormat: '',
59
+ showPassword: false,
60
+ multiple: false,
61
+ inputHeight: 40,
62
+ isBorder: false,
63
+ disabledDate: undefined,
64
+ marginBottom: 20,
65
+ paddingTop: 20
66
+ })
67
+
68
+ const emit = defineEmits<{
69
+ (event: 'input', value: OutlinedDatePickerValue | ''): void
70
+ (event: 'update:value', value: OutlinedDatePickerValue | ''): void
71
+ (event: 'change', value: OutlinedDatePickerValue | ''): void
72
+ (event: 'focus', value: FocusEvent): void
73
+ (event: 'blur', value: FocusEvent): void
74
+ (event: 'visible-change', value: boolean): void
75
+ }>()
76
+
77
+ const attrs = useAttrs()
78
+ const pickerRef = ref<any>(null)
79
+ const pickerValue = ref<OutlinedDatePickerValue | ''>(props.value ?? '')
80
+ const focused = ref(false)
81
+ const blurred = ref(false)
82
+ const visible = ref(false)
83
+
84
+ watch(
85
+ () => props.value,
86
+ (value) => {
87
+ pickerValue.value = value ?? ''
88
+ },
89
+ { immediate: true }
90
+ )
91
+
92
+ const rootClass = computed(() => attrs.class)
93
+ const rootStyle = computed(() => attrs.style)
94
+
95
+ const resolvedValueFormat = computed(() => props.valueFormat || props.format)
96
+
97
+ const resolvedDisabledDate = computed(() => createDisabledDateHandler(props.disabledDate))
98
+
99
+ const resolvedDefaultTime = computed(() => {
100
+ if (attrs.defaultTime !== undefined) {
101
+ return attrs.defaultTime
102
+ }
103
+
104
+ if ((attrs as Record<string, unknown>)['default-time'] !== undefined) {
105
+ return (attrs as Record<string, unknown>)['default-time']
106
+ }
107
+
108
+ return props.timeValue ? new Date().setHours(0, 0, 0, 0) : new Date()
109
+ })
110
+
111
+ const resolvedDisabledHours = computed(() => {
112
+ if (attrs.disabledHours !== undefined) {
113
+ return attrs.disabledHours
114
+ }
115
+
116
+ if ((attrs as Record<string, unknown>)['disabled-hours'] !== undefined) {
117
+ return (attrs as Record<string, unknown>)['disabled-hours']
118
+ }
119
+
120
+ if (!shouldLimitCurrentTime()) {
121
+ return undefined
122
+ }
123
+
124
+ return () => {
125
+ const hours = new Date().getHours()
126
+ return Array.from({ length: hours }, (_, index) => index)
127
+ }
128
+ })
129
+
130
+ const resolvedDisabledMinutes = computed(() => {
131
+ if (attrs.disabledMinutes !== undefined) {
132
+ return attrs.disabledMinutes
133
+ }
134
+
135
+ if ((attrs as Record<string, unknown>)['disabled-minutes'] !== undefined) {
136
+ return (attrs as Record<string, unknown>)['disabled-minutes']
137
+ }
138
+
139
+ if (!shouldLimitCurrentTime()) {
140
+ return undefined
141
+ }
142
+
143
+ return () => {
144
+ const minutes = new Date().getMinutes()
145
+ return Array.from({ length: minutes }, (_, index) => index)
146
+ }
147
+ })
148
+
149
+ const pickerAttrs = computed(() => {
150
+ const { class: _class, style: _style, ...rest } = attrs
151
+
152
+ return {
153
+ ...rest,
154
+ defaultTime: resolvedDefaultTime.value,
155
+ disabledDate: resolvedDisabledDate.value,
156
+ disabledHours: resolvedDisabledHours.value,
157
+ disabledMinutes: resolvedDisabledMinutes.value
158
+ }
159
+ })
160
+
161
+ const containerClasses = computed(() => ({
162
+ 'outlined-date-picker--bordered': props.isBorder
163
+ }))
164
+
165
+ const containerStyle = computed(() => ({
166
+ marginBottom: toCssValue(props.marginBottom),
167
+ paddingTop: toCssValue(props.paddingTop)
168
+ }))
169
+
170
+ const pickerStyle = computed(() => ({
171
+ height: `${props.inputHeight}px`
172
+ }))
173
+
174
+ const floatingLabel = computed(() => props.label || props.placeholder || '')
175
+
176
+ const hasContent = computed(() => hasFilledValue(pickerValue.value))
177
+
178
+ const labelClasses = computed(() => ({
179
+ 'outlined-date-picker__label--floating': focused.value || hasContent.value,
180
+ 'outlined-date-picker__label--placeholder': !focused.value && !hasContent.value,
181
+ 'outlined-date-picker__label--filled': hasContent.value && !focused.value
182
+ }))
183
+
184
+ function handleModelValueUpdate(value: OutlinedDatePickerValue) {
185
+ const normalizedValue = normalizeValue(value)
186
+ pickerValue.value = normalizedValue
187
+ emitModelValue(normalizedValue)
188
+ }
189
+
190
+ function handleChange(value: OutlinedDatePickerValue) {
191
+ const normalizedValue = normalizeValue(value)
192
+ pickerValue.value = normalizedValue
193
+ emit('change', normalizedValue)
194
+ }
195
+
196
+ function handleFocus(event: FocusEvent) {
197
+ blurred.value = false
198
+ focused.value = true
199
+ emit('focus', event)
200
+ }
201
+
202
+ function handleBlur(event: FocusEvent) {
203
+ blurred.value = true
204
+
205
+ if (!visible.value) {
206
+ focused.value = false
207
+ emitModelValue(pickerValue.value)
208
+ }
209
+
210
+ emit('blur', event)
211
+ }
212
+
213
+ function handleVisibleChange(nextVisible: boolean) {
214
+ visible.value = nextVisible
215
+
216
+ if (!nextVisible && blurred.value) {
217
+ focused.value = false
218
+ }
219
+
220
+ emit('visible-change', nextVisible)
221
+ }
222
+
223
+ function emitModelValue(value: OutlinedDatePickerValue | '') {
224
+ emit('input', value)
225
+ emit('update:value', value)
226
+ }
227
+
228
+ function normalizeValue(value: OutlinedDatePickerValue | undefined) {
229
+ return value ?? ''
230
+ }
231
+
232
+ function createDisabledDateHandler(disabledDate: OutlinedDatePickerDisabledDate | undefined) {
233
+ if (!disabledDate) {
234
+ return undefined
235
+ }
236
+
237
+ if (typeof disabledDate === 'function') {
238
+ return disabledDate
239
+ }
240
+
241
+ const boundary = normalizeBoundary(disabledDate)
242
+
243
+ return (value: Date) => value.getTime() < boundary - 86400000
244
+ }
245
+
246
+ function normalizeBoundary(value: Exclude<OutlinedDatePickerDisabledDate, ((date: Date) => boolean)>) {
247
+ if (value === 'nowDate') {
248
+ return Date.now()
249
+ }
250
+
251
+ if (value instanceof Date) {
252
+ return value.getTime()
253
+ }
254
+
255
+ if (typeof value === 'number') {
256
+ return value
257
+ }
258
+
259
+ return new Date(value).getTime()
260
+ }
261
+
262
+ function shouldLimitCurrentTime() {
263
+ return Boolean(props.disabledDate) && props.typeDate.toLowerCase().includes('time')
264
+ }
265
+
266
+ function hasFilledValue(value: unknown) {
267
+ if (Array.isArray(value)) {
268
+ return value.length > 0
269
+ }
270
+
271
+ return value !== undefined && value !== null && String(value) !== ''
272
+ }
273
+
274
+ function toCssValue(value: OutlinedDatePickerCssValue | undefined) {
275
+ if (typeof value === 'number') {
276
+ return `${value}px`
277
+ }
278
+
279
+ return value
280
+ }
281
+
282
+ defineExpose<OutlinedDatePickerExpose>({
283
+ focus: () => {
284
+ pickerRef.value?.focus?.()
285
+ },
286
+ blur: () => {
287
+ pickerRef.value?.blur?.()
288
+ },
289
+ handleOpen: () => {
290
+ pickerRef.value?.handleOpen?.()
291
+ },
292
+ handleClose: () => {
293
+ pickerRef.value?.handleClose?.()
294
+ },
295
+ getPickerRef: () => pickerRef.value
296
+ })
297
+ </script>
298
+
299
+ <style scoped>
300
+ .outlined-date-picker {
301
+ position: relative;
302
+ }
303
+
304
+ .outlined-date-picker__label {
305
+ position: absolute;
306
+ left: -2px;
307
+ padding: 2px 5px;
308
+ border-radius: 2px;
309
+ background-color: #ffffff;
310
+ font-size: 12px;
311
+ line-height: 1;
312
+ transition: color 200ms cubic-bezier(0, 0, 0.2, 1), transform 200ms cubic-bezier(0, 0, 0.2, 1);
313
+ pointer-events: none;
314
+ }
315
+
316
+ .outlined-date-picker__label--floating {
317
+ top: 18px;
318
+ z-index: 1;
319
+ opacity: 1;
320
+ transform: translate(14px, -6px) scale(1);
321
+ color: var(--el-color-primary);
322
+ }
323
+
324
+ .outlined-date-picker__label--placeholder {
325
+ top: 50%;
326
+ left: 1px;
327
+ z-index: -1;
328
+ opacity: 0;
329
+ transform: translate(14px, 6px) scale(1);
330
+ }
331
+
332
+ .outlined-date-picker__label--filled {
333
+ color: var(--el-text-color-regular);
334
+ }
335
+
336
+ :deep(.outlined-date-picker__control.el-date-editor) {
337
+ width: 100%;
338
+ }
339
+
340
+ :deep(.outlined-date-picker__control .el-input__wrapper) {
341
+ position: relative;
342
+ min-height: inherit;
343
+ height: inherit;
344
+ }
345
+
346
+ :deep(.outlined-date-picker__control .el-input__prefix) {
347
+ position: absolute;
348
+ right: 0;
349
+ }
350
+
351
+ :deep(.outlined-date-picker__control .el-input__suffix) {
352
+ position: absolute;
353
+ right: 30px;
354
+ }
355
+
356
+ :deep(.outlined-date-picker--bordered .el-input__wrapper) {
357
+ box-shadow: none;
358
+ border-radius: 0;
359
+ border-bottom: 1px solid #cccccc;
360
+ }
361
+
362
+ :deep(.outlined-date-picker--bordered .el-input__wrapper.is-focus) {
363
+ border-bottom-color: var(--el-color-primary);
364
+ }
365
+ </style>
@@ -0,0 +1,137 @@
1
+ # OutlinedDatePicker
2
+
3
+ `OutlinedDatePicker` 用来统一项目里带浮动标签样式的日期选择器,避免页面层重复写日期控件高度、标签悬浮、日期禁用和焦点控制逻辑。
4
+
5
+ ## 解决的问题
6
+
7
+ - 统一浮动标签日期选择器样式
8
+ - 统一 `v-model:value`、`input`、`update:value` 和 `change` 事件约定
9
+ - 统一 `typeDate / format / valueFormat / disabledDate` 配置方式
10
+ - 自动处理弹层展开时的焦点态和浮动标签状态
11
+ - 通过组件实例直接调用 `focus`、`blur`、`handleOpen`、`handleClose`
12
+
13
+ ## 适用场景
14
+
15
+ - 页面需要和 `OutlinedInput / OutlinedSelect` 保持一致视觉语言
16
+ - 表单页中有大量基础日期或日期时间选择器
17
+ - 希望页面只传日期类型和格式配置,不再重复封装统一壳子
18
+ - 需要兼容旧项目里的 `disabledDate="nowDate"` 这类写法
19
+
20
+ ## 不适用场景
21
+
22
+ - 需要复杂日期区间联动逻辑并完全接管弹层行为
23
+ - 不是浮动标签风格的时间控件
24
+ - 页面需要不同于 Element Plus 的日期引擎或交互模式
25
+
26
+ ## 基础用法
27
+
28
+ ```vue
29
+ <template>
30
+ <OutlinedDatePicker
31
+ v-model:value="form.effectDate"
32
+ placeholder="开始生效日期"
33
+ :is-border="true"
34
+ :input-height="40"
35
+ type-date="date"
36
+ />
37
+ </template>
38
+
39
+ <script setup lang="ts">
40
+ import { reactive } from 'vue'
41
+ import { OutlinedDatePicker } from '@hbdlzy/ui-core'
42
+
43
+ const form = reactive({
44
+ effectDate: ''
45
+ })
46
+ </script>
47
+ ```
48
+
49
+ ## 日期时间示例
50
+
51
+ ```vue
52
+ <template>
53
+ <OutlinedDatePicker
54
+ v-model:value="form.effectiveTime"
55
+ placeholder="上传收益数据开始生效时间"
56
+ :is-border="true"
57
+ type-date="datetime"
58
+ format="YYYY-MM-DD HH:mm:ss"
59
+ />
60
+ </template>
61
+
62
+ <script setup lang="ts">
63
+ import { reactive } from 'vue'
64
+ import { OutlinedDatePicker } from '@hbdlzy/ui-core'
65
+
66
+ const form = reactive({
67
+ effectiveTime: ''
68
+ })
69
+ </script>
70
+ ```
71
+
72
+ ## 禁用日期示例
73
+
74
+ ```vue
75
+ <template>
76
+ <OutlinedDatePicker
77
+ v-model:value="form.effectDate"
78
+ placeholder="开始生效日期"
79
+ type-date="date"
80
+ :disabled-date="'nowDate'"
81
+ :is-border="true"
82
+ />
83
+ </template>
84
+
85
+ <script setup lang="ts">
86
+ import { reactive } from 'vue'
87
+ import { OutlinedDatePicker } from '@hbdlzy/ui-core'
88
+
89
+ const form = reactive({
90
+ effectDate: ''
91
+ })
92
+ </script>
93
+ ```
94
+
95
+ ## Props
96
+
97
+ - `value`: 当前值,推荐通过 `v-model:value` 使用
98
+ - `placeholder`: 占位文案,同时默认作为浮动标签文案
99
+ - `label`: 自定义浮动标签文案,未传时回退到 `placeholder`
100
+ - `disabled`: 是否禁用
101
+ - `timeValue`: 是否按带时间场景处理默认时间
102
+ - `typeDate`: 日期控件类型,默认 `date`
103
+ - `format`: 展示格式,默认 `YYYY-MM-DD`
104
+ - `valueFormat`: 值格式,默认回退到 `format`
105
+ - `inputHeight`: 控件高度,默认 `40`
106
+ - `isBorder`: 是否启用底部边框样式
107
+ - `disabledDate`: 日期禁用规则,支持 `nowDate`、日期字符串、时间戳、`Date` 或函数
108
+ - `marginBottom`: 外层容器底部间距,默认 `20`
109
+ - `paddingTop`: 外层容器顶部留白,默认 `20`
110
+
111
+ ## Events
112
+
113
+ - `input`: 值变化时触发
114
+ - `update:value`: `v-model:value` 对应更新事件
115
+ - `change`: 用户确认变更时触发
116
+ - `focus`: 聚焦时触发
117
+ - `blur`: 失焦时触发
118
+ - `visible-change`: 面板展开状态变化时触发
119
+
120
+ ## Expose
121
+
122
+ - `focus`: 手动聚焦日期控件
123
+ - `blur`: 手动失焦日期控件
124
+ - `handleOpen`: 手动打开日期面板
125
+ - `handleClose`: 手动关闭日期面板
126
+ - `getPickerRef`: 获取底层 `el-date-picker` 实例
127
+
128
+ ## 透传规则
129
+
130
+ - 未被组件显式声明的属性,会自动透传给内部 `el-date-picker`
131
+ - 适合继续传 `clearable`、`editable`、`default-time`、`shortcuts`、`unlink-panels` 等 Element Plus 原生能力
132
+
133
+ ## 推荐约定
134
+
135
+ - 普通日期和日期时间选择器优先使用 `OutlinedDatePicker`
136
+ - 默认通过 `typeDate + format` 收敛常见场景
137
+ - 自定义日期禁用规则优先使用 `disabledDate`,不要在页面层重复封装相同逻辑
@@ -0,0 +1,11 @@
1
+ import OutlinedDatePicker from './OutlinedDatePicker.vue'
2
+
3
+ export default OutlinedDatePicker
4
+
5
+ export type {
6
+ OutlinedDatePickerCssValue,
7
+ OutlinedDatePickerDisabledDate,
8
+ OutlinedDatePickerExpose,
9
+ OutlinedDatePickerProps,
10
+ OutlinedDatePickerValue
11
+ } from './OutlinedDatePicker.types'
@@ -0,0 +1,34 @@
1
+ export type OutlinedDateTimePickerCssValue = string | number
2
+
3
+ export type OutlinedDateTimePickerValue = string
4
+
5
+ export type OutlinedDateTimePickerDisabledDate =
6
+ | string
7
+ | number
8
+ | Date
9
+ | ((date: Date) => boolean)
10
+
11
+ export interface OutlinedDateTimePickerProps {
12
+ value?: OutlinedDateTimePickerValue
13
+ placeholder?: string
14
+ label?: string
15
+ inputHeight?: number
16
+ isBorder?: boolean
17
+ disabledDate?: OutlinedDateTimePickerDisabledDate
18
+ separatorText?: string
19
+ timePlaceholder?: string
20
+ clearable?: boolean
21
+ marginBottom?: OutlinedDateTimePickerCssValue
22
+ paddingTop?: OutlinedDateTimePickerCssValue
23
+ }
24
+
25
+ export interface OutlinedDateTimePickerExpose {
26
+ focusDate: () => void
27
+ focusTime: () => void
28
+ blurDate: () => void
29
+ blurTime: () => void
30
+ openDatePanel: () => void
31
+ openTimePanel: () => void
32
+ getDatePickerRef: () => unknown | null
33
+ getTimePickerRef: () => unknown | null
34
+ }