@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
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .base-card[data-v-6bfddf1c]{display:flex;flex-direction:column;min-width:0;min-height:0;padding:var(--base-card-padding);border:1px solid var(--base-card-border-color);background:var(--base-card-background-color);box-shadow:0 0 2px 0 var(--base-card-shadow-color);border-radius:var(--base-card-radius)}.base-card__header[data-v-6bfddf1c]{display:flex;align-items:center;justify-content:space-between;gap:var(--base-card-header-gap);margin-bottom:var(--base-card-header-margin-bottom);min-height:24px;flex-wrap:wrap}.base-card__header-left[data-v-6bfddf1c],.base-card__header-right[data-v-6bfddf1c]{display:flex;align-items:center;min-width:0;flex-wrap:wrap}.base-card__header-left[data-v-6bfddf1c]{flex:1 1 auto;gap:var(--base-card-left-gap)}.base-card__header-right[data-v-6bfddf1c]{flex:0 1 auto;justify-content:flex-end;gap:var(--base-card-right-gap)}.base-card__title-wrap[data-v-6bfddf1c]{display:flex;align-items:center;min-width:0}.base-card__title-marker[data-v-6bfddf1c]{width:var(--base-card-marker-width);height:var(--base-card-marker-height);background:var(--base-card-marker-color);border-radius:var(--base-card-marker-radius);margin-right:var(--base-card-marker-gap);flex:0 0 auto}.base-card__title[data-v-6bfddf1c]{margin:0;min-width:0;color:var(--base-card-title-color);font-size:var(--base-card-title-size);font-weight:var(--base-card-title-weight);line-height:1.4;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.base-card__meta[data-v-6bfddf1c]{color:var(--base-card-meta-color);font-size:var(--base-card-meta-size);line-height:1.4}.base-card__export[data-v-6bfddf1c]{border:none;background:transparent;padding:0;color:var(--base-card-export-color);cursor:pointer;font-size:var(--base-card-meta-size);line-height:1.4}.base-card__export[data-v-6bfddf1c]:disabled{opacity:.5;cursor:not-allowed}.base-card__body[data-v-6bfddf1c]{display:flex;flex:1 1 auto;flex-direction:column;min-height:0}.base-card__tip[data-v-6bfddf1c]{flex:0 0 auto}.base-card__content[data-v-6bfddf1c]{flex:1 1 auto;min-height:0}.base-table[data-v-429ba785]{display:flex;flex-direction:column;height:100%;min-height:0}.base-table__toolbar[data-v-429ba785]{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:16px;min-height:32px;flex-wrap:wrap}.base-table__toolbar-left[data-v-429ba785]{flex:1 1 auto;min-width:0}.base-table__toolbar-right[data-v-429ba785]{flex:0 1 auto}.base-table__actions[data-v-429ba785]{display:flex;align-items:center;gap:4px;flex-wrap:wrap}.base-table__link[data-v-429ba785]{padding:0;border:none;background:transparent;color:var(--el-color-primary);cursor:pointer}.base-table__text[data-v-429ba785]{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.base-table__tags[data-v-429ba785]{display:flex;align-items:center;gap:4px;flex-wrap:wrap}.base-table__image[data-v-429ba785]{border-radius:4px;overflow:hidden}.base-echart[data-v-51bc4772],.base-echart__inner[data-v-51bc4772]{width:100%;height:100%}.outlined-input[data-v-2bf89ccd]{position:relative}.outlined-input__label[data-v-2bf89ccd]{position:absolute;left:-2px;padding:2px 5px;border-radius:2px;background-color:#fff;font-size:12px;line-height:1;transition:color .2s cubic-bezier(0,0,.2,1),transform .2s cubic-bezier(0,0,.2,1);pointer-events:none}.outlined-input__label--floating[data-v-2bf89ccd]{top:18px;z-index:1;opacity:1;transform:translate(14px,-6px) scale(1);color:var(--el-color-primary)}.outlined-input__label--placeholder[data-v-2bf89ccd]{top:50%;left:1px;z-index:-1;opacity:0;transform:translate(14px,6px) scale(1)}.outlined-input__label--filled[data-v-2bf89ccd]{color:var(--el-text-color-regular)}.outlined-input__suffix-text[data-v-2bf89ccd]{white-space:nowrap}[data-v-2bf89ccd] .outlined-input--bordered .el-input__wrapper{box-shadow:none;border-radius:0;border-bottom:1px solid #cccccc}[data-v-2bf89ccd] .outlined-input--bordered .el-input__wrapper.is-focus{border-bottom-color:var(--el-color-primary)}[data-v-2bf89ccd] .outlined-input--bordered .el-textarea__inner{border-width:0 0 1px;border-radius:0;box-shadow:none}[data-v-2bf89ccd] .outlined-input--bordered .el-textarea__inner:focus{border-bottom-color:var(--el-color-primary)}[data-v-2bf89ccd] .outlined-input--textarea-default .el-textarea__inner{min-height:100px!important;max-height:180px}.outlined-select[data-v-7bd2b5f2]{position:relative}.outlined-select__label[data-v-7bd2b5f2]{position:absolute;left:-2px;padding:2px 5px;border-radius:2px;background-color:#fff;font-size:12px;line-height:1;transition:color .2s cubic-bezier(0,0,.2,1),transform .2s cubic-bezier(0,0,.2,1);pointer-events:none}.outlined-select__label--floating[data-v-7bd2b5f2]{top:18px;z-index:1;opacity:1;transform:translate(14px,-6px) scale(1);color:var(--el-color-primary)}.outlined-select__label--placeholder[data-v-7bd2b5f2]{top:50%;left:1px;z-index:-1;opacity:0;transform:translate(14px,6px) scale(1)}.outlined-select__label--filled[data-v-7bd2b5f2]{color:var(--el-text-color-regular)}[data-v-7bd2b5f2] .outlined-select__control.el-select{width:100%}[data-v-7bd2b5f2] .outlined-select__control .el-select__wrapper{min-height:inherit;height:inherit}[data-v-7bd2b5f2] .outlined-select__control .el-select__tags{padding-top:4px}[data-v-7bd2b5f2] .outlined-select__control .el-select__selection{min-height:inherit}[data-v-7bd2b5f2] .outlined-select--bordered .el-select__wrapper{box-shadow:none;border-radius:0;border-bottom:1px solid #cccccc}[data-v-7bd2b5f2] .outlined-select--bordered .el-select__wrapper.is-focused{border-bottom-color:var(--el-color-primary)}.outlined-date-picker[data-v-c95a293c]{position:relative}.outlined-date-picker__label[data-v-c95a293c]{position:absolute;left:-2px;padding:2px 5px;border-radius:2px;background-color:#fff;font-size:12px;line-height:1;transition:color .2s cubic-bezier(0,0,.2,1),transform .2s cubic-bezier(0,0,.2,1);pointer-events:none}.outlined-date-picker__label--floating[data-v-c95a293c]{top:18px;z-index:1;opacity:1;transform:translate(14px,-6px) scale(1);color:var(--el-color-primary)}.outlined-date-picker__label--placeholder[data-v-c95a293c]{top:50%;left:1px;z-index:-1;opacity:0;transform:translate(14px,6px) scale(1)}.outlined-date-picker__label--filled[data-v-c95a293c]{color:var(--el-text-color-regular)}[data-v-c95a293c] .outlined-date-picker__control.el-date-editor{width:100%}[data-v-c95a293c] .outlined-date-picker__control .el-input__wrapper{position:relative;min-height:inherit;height:inherit}[data-v-c95a293c] .outlined-date-picker__control .el-input__prefix{position:absolute;right:0}[data-v-c95a293c] .outlined-date-picker__control .el-input__suffix{position:absolute;right:30px}[data-v-c95a293c] .outlined-date-picker--bordered .el-input__wrapper{box-shadow:none;border-radius:0;border-bottom:1px solid #cccccc}[data-v-c95a293c] .outlined-date-picker--bordered .el-input__wrapper.is-focus{border-bottom-color:var(--el-color-primary)}.outlined-date-time-picker[data-v-f2fefd22]{display:flex;align-items:center;gap:8px}.outlined-date-time-picker__field[data-v-f2fefd22]{position:relative;flex:1}.outlined-date-time-picker__separator[data-v-f2fefd22]{flex:0 0 auto;color:var(--el-text-color-regular)}.outlined-date-time-picker__label[data-v-f2fefd22]{position:absolute;left:-2px;padding:2px 5px;border-radius:2px;background-color:#fff;font-size:12px;line-height:1;transition:color .2s cubic-bezier(0,0,.2,1),transform .2s cubic-bezier(0,0,.2,1);pointer-events:none}.outlined-date-time-picker__label--floating[data-v-f2fefd22]{top:18px;z-index:1;opacity:1;transform:translate(14px,-6px) scale(1);color:var(--el-color-primary)}.outlined-date-time-picker__label--placeholder[data-v-f2fefd22]{top:50%;left:1px;z-index:-1;opacity:0;transform:translate(14px,6px) scale(1)}.outlined-date-time-picker__label--filled[data-v-f2fefd22]{color:var(--el-text-color-regular)}[data-v-f2fefd22] .outlined-date-time-picker__control.el-date-editor{width:100%}[data-v-f2fefd22] .outlined-date-time-picker__control .el-input__wrapper{position:relative;min-height:inherit;height:inherit}[data-v-f2fefd22] .outlined-date-time-picker__control .el-input__prefix{position:absolute;right:0}[data-v-f2fefd22] .outlined-date-time-picker__control .el-input__suffix{position:absolute;right:30px}[data-v-f2fefd22] .outlined-date-time-picker__field--bordered .el-input__wrapper{box-shadow:none;border-radius:0;border-bottom:1px solid #cccccc}[data-v-f2fefd22] .outlined-date-time-picker__field--bordered .el-input__wrapper.is-focus{border-bottom-color:var(--el-color-primary)}.outlined-time-picker[data-v-6a467b88]{position:relative}.outlined-time-picker__label[data-v-6a467b88]{position:absolute;left:-2px;padding:2px 5px;border-radius:2px;background-color:#fff;font-size:12px;line-height:1;transition:color .2s cubic-bezier(0,0,.2,1),transform .2s cubic-bezier(0,0,.2,1);pointer-events:none}.outlined-time-picker__label--floating[data-v-6a467b88]{top:18px;z-index:1;opacity:1;transform:translate(14px,-6px) scale(1);color:var(--el-color-primary)}.outlined-time-picker__label--placeholder[data-v-6a467b88]{top:50%;left:1px;z-index:-1;opacity:0;transform:translate(14px,6px) scale(1)}.outlined-time-picker__label--filled[data-v-6a467b88]{color:var(--el-text-color-regular)}[data-v-6a467b88] .outlined-time-picker__control.el-date-editor{width:100%}[data-v-6a467b88] .outlined-time-picker__control .el-input__wrapper{position:relative;min-height:inherit;height:inherit}[data-v-6a467b88] .outlined-time-picker__control .el-input__prefix{position:absolute;right:0}[data-v-6a467b88] .outlined-time-picker__control .el-input__suffix{position:absolute;right:30px}[data-v-6a467b88] .outlined-time-picker--bordered .el-input__wrapper{box-shadow:none;border-radius:0;border-bottom:1px solid #cccccc}[data-v-6a467b88] .outlined-time-picker--bordered .el-input__wrapper.is-focus{border-bottom-color:var(--el-color-primary)}.outlined-cascader[data-v-2c93c0e4]{position:relative}.outlined-cascader__label[data-v-2c93c0e4]{position:absolute;left:-2px;padding:2px 5px;border-radius:2px;background-color:#fff;font-size:12px;line-height:1;transition:color .2s cubic-bezier(0,0,.2,1),transform .2s cubic-bezier(0,0,.2,1);pointer-events:none}.outlined-cascader__label--floating[data-v-2c93c0e4]{top:18px;z-index:1;opacity:1;transform:translate(14px,-6px) scale(1);color:var(--el-color-primary)}.outlined-cascader__label--placeholder[data-v-2c93c0e4]{top:50%;left:1px;z-index:-1;opacity:0;transform:translate(14px,6px) scale(1)}.outlined-cascader__label--filled[data-v-2c93c0e4]{color:var(--el-text-color-regular)}[data-v-2c93c0e4] .outlined-cascader__control .el-input{height:100%}[data-v-2c93c0e4] .outlined-cascader--bordered .el-input__wrapper{box-shadow:none;border-radius:0;border-bottom:1px solid #cccccc}[data-v-2c93c0e4] .outlined-cascader--bordered .el-input__wrapper.is-focus{border-bottom-color:var(--el-color-primary)}.outlined-tree-select[data-v-8403e6ea]{position:relative}.outlined-tree-select__label[data-v-8403e6ea]{position:absolute;left:-2px;padding:2px 5px;border-radius:2px;background-color:#fff;font-size:12px;line-height:1;transition:color .2s cubic-bezier(0,0,.2,1),transform .2s cubic-bezier(0,0,.2,1);pointer-events:none}.outlined-tree-select__label--floating[data-v-8403e6ea]{top:18px;z-index:1;opacity:1;transform:translate(14px,-6px) scale(1);color:var(--el-color-primary)}.outlined-tree-select__label--placeholder[data-v-8403e6ea]{top:50%;left:1px;z-index:-1;opacity:0;transform:translate(14px,6px) scale(1)}.outlined-tree-select__label--filled[data-v-8403e6ea]{color:var(--el-text-color-regular)}[data-v-8403e6ea] .outlined-tree-select__control.el-select{width:100%}[data-v-8403e6ea] .outlined-tree-select__control .el-select__wrapper{min-height:inherit;height:inherit}[data-v-8403e6ea] .outlined-tree-select__control .el-select__selection{min-height:inherit}[data-v-8403e6ea] .outlined-tree-select__control .el-select__tags{padding-top:4px}[data-v-8403e6ea] .outlined-tree-select--bordered .el-select__wrapper{box-shadow:none;border-radius:0;border-bottom:1px solid #cccccc}[data-v-8403e6ea] .outlined-tree-select--bordered .el-select__wrapper.is-focused{border-bottom-color:var(--el-color-primary)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hbdlzy/ui-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Company shared Vue UI core package.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,8 +30,10 @@
30
30
  "vue": "^3.5.14"
31
31
  },
32
32
  "dependencies": {
33
- "file-saver": "2.0.5",
34
- "xlsx": "^0.18.5"
33
+ "dayjs": "^1.11.13",
34
+ "echarts": "^5.5.1",
35
+ "exceljs": "^4.4.0",
36
+ "file-saver": "2.0.5"
35
37
  },
36
38
  "sideEffects": [
37
39
  "**/*.css",
@@ -0,0 +1,38 @@
1
+ export type BaseCardCssValue = string | number
2
+
3
+ export interface BaseCardDefaultExportPayload {
4
+ title?: string
5
+ metaText?: string
6
+ }
7
+
8
+ export interface BaseCardProps {
9
+ title?: string
10
+ metaText?: string
11
+ showHeader?: boolean
12
+ showRight?: boolean
13
+ showTitleMarker?: boolean
14
+ showExport?: boolean
15
+ exportText?: string
16
+ exportDisabled?: boolean
17
+ exportPayload?: unknown
18
+ padding?: BaseCardCssValue
19
+ radius?: BaseCardCssValue
20
+ borderColor?: string
21
+ backgroundColor?: string
22
+ shadowColor?: string
23
+ headerGap?: BaseCardCssValue
24
+ headerMarginBottom?: BaseCardCssValue
25
+ leftGap?: BaseCardCssValue
26
+ rightGap?: BaseCardCssValue
27
+ titleColor?: string
28
+ titleSize?: BaseCardCssValue
29
+ titleWeight?: string | number
30
+ markerColor?: string
31
+ markerWidth?: BaseCardCssValue
32
+ markerHeight?: BaseCardCssValue
33
+ markerRadius?: BaseCardCssValue
34
+ markerGap?: BaseCardCssValue
35
+ metaColor?: string
36
+ metaSize?: BaseCardCssValue
37
+ exportColor?: string
38
+ }
@@ -0,0 +1,250 @@
1
+ <template>
2
+ <div class="base-card" :style="cardStyles">
3
+ <div v-if="shouldRenderHeader" class="base-card__header">
4
+ <div class="base-card__header-left">
5
+ <div v-if="hasTitleBlock" class="base-card__title-wrap">
6
+ <span v-if="showTitleMarker" class="base-card__title-marker"></span>
7
+ <slot v-if="$slots['title-label']" name="title-label"></slot>
8
+ <h4 v-else class="base-card__title">{{ title }}</h4>
9
+ </div>
10
+ <slot name="title"></slot>
11
+ </div>
12
+
13
+ <div v-if="hasRightSection" class="base-card__header-right">
14
+ <slot name="header-right"></slot>
15
+ <span v-if="metaText" class="base-card__meta">{{ metaText }}</span>
16
+ <button
17
+ v-if="showExport"
18
+ class="base-card__export"
19
+ type="button"
20
+ :disabled="exportDisabled"
21
+ @click="handleExport"
22
+ >
23
+ {{ exportText }}
24
+ </button>
25
+ </div>
26
+ </div>
27
+
28
+ <div class="base-card__body">
29
+ <div v-if="$slots.tip" class="base-card__tip">
30
+ <slot name="tip"></slot>
31
+ </div>
32
+ <div class="base-card__content">
33
+ <slot></slot>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup lang="ts">
40
+ import { computed, useSlots } from 'vue'
41
+ import type { BaseCardCssValue, BaseCardDefaultExportPayload, BaseCardProps } from './BaseCard.types'
42
+
43
+ defineOptions({
44
+ name: 'BaseCard'
45
+ })
46
+
47
+ const props = withDefaults(defineProps<BaseCardProps>(), {
48
+ title: '',
49
+ metaText: '',
50
+ showHeader: true,
51
+ showRight: true,
52
+ showTitleMarker: true,
53
+ showExport: false,
54
+ exportText: '导出',
55
+ exportDisabled: false,
56
+ padding: 20,
57
+ radius: 8,
58
+ borderColor: 'var(--border-color)',
59
+ backgroundColor: 'var(--border-bg-color)',
60
+ shadowColor: 'var(--border-projection)',
61
+ headerGap: 16,
62
+ headerMarginBottom: 20,
63
+ leftGap: 16,
64
+ rightGap: 16,
65
+ titleColor: 'var(--el-text-color-primary)',
66
+ titleSize: 18,
67
+ titleWeight: 500,
68
+ markerColor: 'var(--el-color-primary)',
69
+ markerWidth: 4,
70
+ markerHeight: 24,
71
+ markerRadius: '0 4px 4px 0',
72
+ markerGap: 10,
73
+ metaColor: '#808080',
74
+ metaSize: 16,
75
+ exportColor: 'var(--el-color-primary)'
76
+ })
77
+
78
+ const emit = defineEmits<{
79
+ (event: 'export', payload?: unknown): void
80
+ }>()
81
+
82
+ const slots = useSlots()
83
+
84
+ const hasTitleBlock = computed(() => Boolean(props.title) || Boolean(slots['title-label']))
85
+ const hasTitleExtra = computed(() => Boolean(slots.title))
86
+ const hasRightExtra = computed(() => Boolean(slots['header-right']))
87
+
88
+ const hasRightSection = computed(
89
+ () => props.showRight && (hasRightExtra.value || Boolean(props.metaText) || props.showExport)
90
+ )
91
+
92
+ const shouldRenderHeader = computed(
93
+ () => props.showHeader && (hasTitleBlock.value || hasTitleExtra.value || hasRightSection.value)
94
+ )
95
+
96
+ const defaultExportPayload = computed<BaseCardDefaultExportPayload>(() => ({
97
+ title: props.title || undefined,
98
+ metaText: props.metaText || undefined
99
+ }))
100
+
101
+ const cardStyles = computed<Record<string, string | undefined>>(() => ({
102
+ '--base-card-padding': toCssValue(props.padding),
103
+ '--base-card-radius': toCssValue(props.radius),
104
+ '--base-card-border-color': props.borderColor,
105
+ '--base-card-background-color': props.backgroundColor,
106
+ '--base-card-shadow-color': props.shadowColor,
107
+ '--base-card-header-gap': toCssValue(props.headerGap),
108
+ '--base-card-header-margin-bottom': toCssValue(props.headerMarginBottom),
109
+ '--base-card-left-gap': toCssValue(props.leftGap),
110
+ '--base-card-right-gap': toCssValue(props.rightGap),
111
+ '--base-card-title-color': props.titleColor,
112
+ '--base-card-title-size': toCssValue(props.titleSize),
113
+ '--base-card-title-weight': toTextValue(props.titleWeight),
114
+ '--base-card-marker-color': props.markerColor,
115
+ '--base-card-marker-width': toCssValue(props.markerWidth),
116
+ '--base-card-marker-height': toCssValue(props.markerHeight),
117
+ '--base-card-marker-radius': toCssValue(props.markerRadius),
118
+ '--base-card-marker-gap': toCssValue(props.markerGap),
119
+ '--base-card-meta-color': props.metaColor,
120
+ '--base-card-meta-size': toCssValue(props.metaSize),
121
+ '--base-card-export-color': props.exportColor
122
+ }))
123
+
124
+ const handleExport = () => {
125
+ emit('export', props.exportPayload === undefined ? defaultExportPayload.value : props.exportPayload)
126
+ }
127
+
128
+ function toCssValue(value?: BaseCardCssValue, unit = 'px') {
129
+ if (value === undefined || value === null || value === '') {
130
+ return undefined
131
+ }
132
+
133
+ return typeof value === 'number' ? `${value}${unit}` : value
134
+ }
135
+
136
+ function toTextValue(value?: string | number) {
137
+ if (value === undefined || value === null || value === '') {
138
+ return undefined
139
+ }
140
+
141
+ return String(value)
142
+ }
143
+ </script>
144
+
145
+ <style scoped>
146
+ .base-card {
147
+ display: flex;
148
+ flex-direction: column;
149
+ min-width: 0;
150
+ min-height: 0;
151
+ padding: var(--base-card-padding);
152
+ border: 1px solid var(--base-card-border-color);
153
+ background: var(--base-card-background-color);
154
+ box-shadow: 0 0 2px 0 var(--base-card-shadow-color);
155
+ border-radius: var(--base-card-radius);
156
+ }
157
+
158
+ .base-card__header {
159
+ display: flex;
160
+ align-items: center;
161
+ justify-content: space-between;
162
+ gap: var(--base-card-header-gap);
163
+ margin-bottom: var(--base-card-header-margin-bottom);
164
+ min-height: 24px;
165
+ flex-wrap: wrap;
166
+ }
167
+
168
+ .base-card__header-left,
169
+ .base-card__header-right {
170
+ display: flex;
171
+ align-items: center;
172
+ min-width: 0;
173
+ flex-wrap: wrap;
174
+ }
175
+
176
+ .base-card__header-left {
177
+ flex: 1 1 auto;
178
+ gap: var(--base-card-left-gap);
179
+ }
180
+
181
+ .base-card__header-right {
182
+ flex: 0 1 auto;
183
+ justify-content: flex-end;
184
+ gap: var(--base-card-right-gap);
185
+ }
186
+
187
+ .base-card__title-wrap {
188
+ display: flex;
189
+ align-items: center;
190
+ min-width: 0;
191
+ }
192
+
193
+ .base-card__title-marker {
194
+ width: var(--base-card-marker-width);
195
+ height: var(--base-card-marker-height);
196
+ background: var(--base-card-marker-color);
197
+ border-radius: var(--base-card-marker-radius);
198
+ margin-right: var(--base-card-marker-gap);
199
+ flex: 0 0 auto;
200
+ }
201
+
202
+ .base-card__title {
203
+ margin: 0;
204
+ min-width: 0;
205
+ color: var(--base-card-title-color);
206
+ font-size: var(--base-card-title-size);
207
+ font-weight: var(--base-card-title-weight);
208
+ line-height: 1.4;
209
+ white-space: nowrap;
210
+ overflow: hidden;
211
+ text-overflow: ellipsis;
212
+ }
213
+
214
+ .base-card__meta {
215
+ color: var(--base-card-meta-color);
216
+ font-size: var(--base-card-meta-size);
217
+ line-height: 1.4;
218
+ }
219
+
220
+ .base-card__export {
221
+ border: none;
222
+ background: transparent;
223
+ padding: 0;
224
+ color: var(--base-card-export-color);
225
+ cursor: pointer;
226
+ font-size: var(--base-card-meta-size);
227
+ line-height: 1.4;
228
+ }
229
+
230
+ .base-card__export:disabled {
231
+ opacity: 0.5;
232
+ cursor: not-allowed;
233
+ }
234
+
235
+ .base-card__body {
236
+ display: flex;
237
+ flex: 1 1 auto;
238
+ flex-direction: column;
239
+ min-height: 0;
240
+ }
241
+
242
+ .base-card__tip {
243
+ flex: 0 0 auto;
244
+ }
245
+
246
+ .base-card__content {
247
+ flex: 1 1 auto;
248
+ min-height: 0;
249
+ }
250
+ </style>
@@ -0,0 +1,164 @@
1
+ # BaseCard
2
+
3
+ `BaseCard` 用来统一封装项目里反复出现的卡片容器、标题区、右侧信息区和导出触发区,避免页面层重复写相同的布局和样式代码。
4
+
5
+ ## 解决的问题
6
+
7
+ - 页面只传标题和内容就能得到统一卡片样式
8
+ - 内聚边框、背景、圆角、阴影,不再依赖业务项目里的全局 `.border-card`
9
+ - 标题左侧竖杠、标题颜色、右侧信息、导出按钮都可以直接配置
10
+ - 左右两侧都支持插槽扩展,复杂场景不用回退到重复写结构
11
+
12
+ ## 适用场景
13
+
14
+ - 看板类页面里的统计卡片、图表卡片、列表卡片
15
+ - 需要统一标题区布局和风格的容器组件
16
+ - 标题右侧要放时间、按钮、筛选器、单选框的场景
17
+ - 希望把“卡片壳子”从业务组件里抽出来统一复用
18
+
19
+ ## 不适用场景
20
+
21
+ - 页面需要完全脱离统一卡片风格
22
+ - 整个容器结构与标题区布局都要做高度定制
23
+ - 只是简单边框盒子,不需要标题区、插槽区和统一视觉规范
24
+
25
+ ## 基础用法
26
+
27
+ ```vue
28
+ <template>
29
+ <BaseCard title="今日收益趋势">
30
+ <div style="height: 320px">这里放图表或其他内容</div>
31
+ </BaseCard>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import { BaseCard } from '@hbdlzy/ui-core'
36
+ </script>
37
+ ```
38
+
39
+ ## 带右侧信息和导出按钮
40
+
41
+ ```vue
42
+ <template>
43
+ <BaseCard
44
+ title="收益数据"
45
+ meta-text="2026-04-21 10:30"
46
+ show-export
47
+ :export-payload="{ url: '/report/export' }"
48
+ @export="handleExport"
49
+ >
50
+ <div>内容区域</div>
51
+ </BaseCard>
52
+ </template>
53
+
54
+ <script setup lang="ts">
55
+ import { BaseCard } from '@hbdlzy/ui-core'
56
+
57
+ const handleExport = (payload?: unknown) => {
58
+ console.log('export payload', payload)
59
+ }
60
+ </script>
61
+ ```
62
+
63
+ ## 标题区扩展
64
+
65
+ `title` 插槽会出现在左侧标题后面,适合放筛选器、单选框、标签等内容。`header-right` 插槽会出现在右侧信息区里,适合放按钮或自定义操作。
66
+
67
+ ```vue
68
+ <template>
69
+ <BaseCard title="市场信息" meta-text="实时更新">
70
+ <template #title>
71
+ <el-radio-group v-model="tabValue">
72
+ <el-radio-button label="日内" />
73
+ <el-radio-button label="周内" />
74
+ </el-radio-group>
75
+ </template>
76
+
77
+ <template #header-right>
78
+ <el-button text>刷新</el-button>
79
+ </template>
80
+
81
+ <template #tip>
82
+ <div style="text-align: right; color: #808080">单位:万元</div>
83
+ </template>
84
+
85
+ <div>内容区域</div>
86
+ </BaseCard>
87
+ </template>
88
+ ```
89
+
90
+ ## 配置样式
91
+
92
+ ```vue
93
+ <BaseCard
94
+ title="自定义样式"
95
+ marker-color="#23c343"
96
+ marker-height="20px"
97
+ title-color="#ffffff"
98
+ border-color="rgba(35, 195, 67, 0.35)"
99
+ background-color="rgba(5, 18, 35, 0.9)"
100
+ :padding="24"
101
+ >
102
+ <div>内容区域</div>
103
+ </BaseCard>
104
+ ```
105
+
106
+ ## Props
107
+
108
+ - `title`: 标题文本
109
+ - `metaText`: 右侧信息文本,常用于时间、更新时间、单位等
110
+ - `showHeader`: 是否显示标题区
111
+ - `showRight`: 是否显示右侧容器
112
+ - `showTitleMarker`: 是否显示标题左侧竖杠
113
+ - `showExport`: 是否显示内置导出按钮
114
+ - `exportText`: 导出按钮文案,默认 `导出`
115
+ - `exportDisabled`: 是否禁用导出按钮
116
+ - `exportPayload`: 点击导出时回传的自定义载荷
117
+ - `padding`: 卡片内边距
118
+ - `radius`: 卡片圆角
119
+ - `borderColor`: 卡片边框颜色
120
+ - `backgroundColor`: 卡片背景色
121
+ - `shadowColor`: 卡片阴影颜色
122
+ - `headerGap`: 标题区左右块之间的间距
123
+ - `headerMarginBottom`: 标题区和内容区之间的间距
124
+ - `leftGap`: 左侧标题块和扩展插槽之间的间距
125
+ - `rightGap`: 右侧内容之间的间距
126
+ - `titleColor`: 标题颜色
127
+ - `titleSize`: 标题字号
128
+ - `titleWeight`: 标题字重
129
+ - `markerColor`: 标题左侧竖杠颜色
130
+ - `markerWidth`: 标题左侧竖杠宽度
131
+ - `markerHeight`: 标题左侧竖杠高度
132
+ - `markerRadius`: 标题左侧竖杠圆角
133
+ - `markerGap`: 标题左侧竖杠和标题的间距
134
+ - `metaColor`: 右侧信息文字颜色
135
+ - `metaSize`: 右侧信息文字字号
136
+ - `exportColor`: 导出按钮颜色
137
+
138
+ ## Slots
139
+
140
+ - 默认插槽:卡片主体内容
141
+ - `title`: 左侧标题后的扩展区域
142
+ - `title-label`: 自定义标题内容,会替代默认标题文本
143
+ - `header-right`: 右侧操作区扩展
144
+ - `tip`: 内容区顶部提示信息
145
+
146
+ ## Events
147
+
148
+ - `export`: 点击内置导出按钮时触发
149
+
150
+ 如果未传 `exportPayload`,默认会返回:
151
+
152
+ ```ts
153
+ {
154
+ title?: string
155
+ metaText?: string
156
+ }
157
+ ```
158
+
159
+ ## 推荐约定
160
+
161
+ - 页面层优先把统计块、图表块、列表块统一包在 `BaseCard` 里
162
+ - 只要不是强业务容器,就尽量不要在页面层重复写卡片壳子
163
+ - 简单场景直接用 `title + 默认插槽`
164
+ - 需要扩展时优先使用 `title` 和 `header-right` 插槽,而不是复制组件结构
@@ -0,0 +1,5 @@
1
+ import BaseCard from './BaseCard.vue'
2
+
3
+ export default BaseCard
4
+
5
+ export type { BaseCardCssValue, BaseCardDefaultExportPayload, BaseCardProps } from './BaseCard.types'
@@ -0,0 +1,35 @@
1
+ import type {
2
+ EChartInitOptions,
3
+ EChartInstance,
4
+ EChartLoadingOptions,
5
+ EChartOption,
6
+ EChartResizeOptions,
7
+ EChartSetOptionOptions
8
+ } from '../../echarts'
9
+
10
+ export interface BaseEChartProps {
11
+ option?: EChartOption
12
+ theme?: string | Record<string, unknown>
13
+ initOptions?: EChartInitOptions
14
+ setOptionOptions?: boolean | EChartSetOptionOptions
15
+ loading?: boolean
16
+ loadingOptions?: EChartLoadingOptions
17
+ autoResize?: boolean
18
+ useDefaultColors?: boolean
19
+ colorStorageKey?: string
20
+ defaultColors?: string[]
21
+ group?: string
22
+ }
23
+
24
+ export interface BaseEChartExpose {
25
+ getInstance: () => EChartInstance | null
26
+ setOption: (option: EChartOption, setOptionOptions?: boolean | EChartSetOptionOptions) => EChartInstance | null
27
+ resize: (resizeOptions?: EChartResizeOptions) => void
28
+ dispatchAction: (payload: Record<string, unknown>) => void
29
+ showLoading: (type?: string, loadingOptions?: EChartLoadingOptions) => void
30
+ hideLoading: () => void
31
+ clear: () => void
32
+ dispose: () => void
33
+ on: (eventName: string, handler: (...args: any[]) => void, context?: unknown) => void
34
+ off: (eventName?: string, handler?: (...args: any[]) => void) => void
35
+ }