gi-component 0.0.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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -0
  3. package/dist/dist/index.es.js +2241 -0
  4. package/dist/dist/index.es.js.map +1 -0
  5. package/dist/dist/index.umd.js +2 -0
  6. package/dist/dist/index.umd.js.map +1 -0
  7. package/dist/gi.css +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/package.json +56 -0
  10. package/packages/components/button/index.ts +5 -0
  11. package/packages/components/button/src/button.vue +59 -0
  12. package/packages/components/button/src/type.ts +15 -0
  13. package/packages/components/card/index.ts +5 -0
  14. package/packages/components/card/src/card.vue +166 -0
  15. package/packages/components/card/src/type.ts +12 -0
  16. package/packages/components/dialog/index.ts +6 -0
  17. package/packages/components/dialog/src/dialog.ts +87 -0
  18. package/packages/components/dialog/src/dialog.vue +122 -0
  19. package/packages/components/dialog/src/type.ts +16 -0
  20. package/packages/components/edit-table/index.ts +5 -0
  21. package/packages/components/edit-table/src/edit-table.vue +207 -0
  22. package/packages/components/edit-table/src/type.ts +69 -0
  23. package/packages/components/form/index.ts +5 -0
  24. package/packages/components/form/src/form.vue +465 -0
  25. package/packages/components/form/src/type.ts +98 -0
  26. package/packages/components/grid/index.ts +8 -0
  27. package/packages/components/grid/src/context.ts +30 -0
  28. package/packages/components/grid/src/grid-item.vue +143 -0
  29. package/packages/components/grid/src/grid.vue +151 -0
  30. package/packages/components/grid/src/hook/use-index.ts +63 -0
  31. package/packages/components/grid/src/hook/use-responsive-state.ts +66 -0
  32. package/packages/components/grid/src/hook/use-responsive-value.ts +36 -0
  33. package/packages/components/grid/src/interface.ts +74 -0
  34. package/packages/components/grid/src/type.ts +0 -0
  35. package/packages/components/grid/src/utils/global-config.ts +6 -0
  36. package/packages/components/grid/src/utils/index.ts +73 -0
  37. package/packages/components/grid/src/utils/is.ts +9 -0
  38. package/packages/components/grid/src/utils/responsive-observe.ts +135 -0
  39. package/packages/components/input-group/index.ts +5 -0
  40. package/packages/components/input-group/src/input-group.vue +92 -0
  41. package/packages/components/input-group/src/type.ts +1 -0
  42. package/packages/components/input-search/index.ts +5 -0
  43. package/packages/components/input-search/src/input-search.vue +62 -0
  44. package/packages/components/input-search/src/type.ts +6 -0
  45. package/packages/components/page-layout/index.ts +5 -0
  46. package/packages/components/page-layout/src/page-layout.vue +180 -0
  47. package/packages/components/page-layout/src/split-button.vue +106 -0
  48. package/packages/components/page-layout/src/type.ts +12 -0
  49. package/packages/components/table/index.ts +5 -0
  50. package/packages/components/table/src/TableColumn.vue +49 -0
  51. package/packages/components/table/src/table.vue +85 -0
  52. package/packages/components/table/src/type.ts +22 -0
  53. package/packages/components/tabs/index.ts +5 -0
  54. package/packages/components/tabs/src/tabs.vue +148 -0
  55. package/packages/components/tabs/src/type.ts +15 -0
  56. package/packages/components.d.ts +26 -0
  57. package/packages/hooks/index.ts +1 -0
  58. package/packages/hooks/useBemClass.ts +11 -0
  59. package/packages/index.ts +78 -0
  60. package/packages/styles/index.scss +176 -0
  61. package/packages/styles/var.scss +1 -0
  62. package/packages/utils/createSelectDialog.ts +67 -0
  63. package/packages/utils/index.ts +1 -0
@@ -0,0 +1,135 @@
1
+ // https://github.com/ant-design/ant-design/blob/master/components/_util/responsiveObserve.ts
2
+
3
+ export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
4
+ export type BreakpointMap = Partial<Record<Breakpoint, string>>;
5
+ export type ScreenMap = Partial<Record<Breakpoint, boolean>>;
6
+
7
+ export const responsiveArray: Breakpoint[] = [
8
+ 'xxl',
9
+ 'xl',
10
+ 'lg',
11
+ 'md',
12
+ 'sm',
13
+ 'xs'
14
+ ];
15
+
16
+ export const responsiveMap: BreakpointMap = {
17
+ xs: '(max-width: 575px)',
18
+ sm: '(min-width: 576px)',
19
+ md: '(min-width: 768px)',
20
+ lg: '(min-width: 992px)',
21
+ xl: '(min-width: 1200px)',
22
+ xxl: '(min-width: 1600px)'
23
+ };
24
+
25
+ type SubscribeFunc = (
26
+ screens: ScreenMap,
27
+ breakpointChecked: Breakpoint
28
+ ) => void;
29
+
30
+ interface MediaQueryResult {
31
+ matches: boolean;
32
+ }
33
+
34
+ type MediaQueryListener = (matches: MediaQueryResult) => void;
35
+
36
+ let subscribers: Array<{
37
+ token: string;
38
+ func: SubscribeFunc;
39
+ }> = [];
40
+ let subUid = -1;
41
+ let screens = {};
42
+
43
+ const responsiveObserve: {
44
+ matchHandlers: {
45
+ [key: string]: {
46
+ mql: MediaQueryList;
47
+ listener: MediaQueryListener;
48
+ };
49
+ };
50
+ dispatch: (pointMap: ScreenMap, breakpointChecked: Breakpoint) => boolean;
51
+ subscribe: (func: SubscribeFunc) => string;
52
+ unsubscribe: (token: string) => void;
53
+ unregister: () => void;
54
+ register: () => void;
55
+ } = {
56
+ matchHandlers: {},
57
+ dispatch(pointMap: ScreenMap, breakpointChecked: Breakpoint) {
58
+ screens = pointMap;
59
+ if (subscribers.length < 1) {
60
+ return false;
61
+ }
62
+
63
+ subscribers.forEach(item => {
64
+ item.func(screens, breakpointChecked);
65
+ });
66
+
67
+ return true;
68
+ },
69
+ subscribe(func: SubscribeFunc) {
70
+ if (subscribers.length === 0) {
71
+ this.register();
72
+ }
73
+ const token = (++subUid).toString();
74
+ subscribers.push({
75
+ token,
76
+ func
77
+ });
78
+ func(screens, null as unknown as Breakpoint);
79
+ return token;
80
+ },
81
+ unsubscribe(token: string) {
82
+ subscribers = subscribers.filter(item => item.token !== token);
83
+ if (subscribers.length === 0) {
84
+ this.unregister();
85
+ }
86
+ },
87
+ unregister() {
88
+ (Object.keys(responsiveMap) as Breakpoint[]).forEach(
89
+ (screen: Breakpoint) => {
90
+ const matchMediaQuery = responsiveMap[screen];
91
+ if (!matchMediaQuery) return;
92
+ const handler = this.matchHandlers[matchMediaQuery];
93
+ if (handler && handler.mql && handler.listener) {
94
+ if (handler.mql.removeEventListener) {
95
+ handler.mql.removeEventListener('change', handler.listener);
96
+ } else {
97
+ handler.mql.removeListener(handler.listener);
98
+ }
99
+ }
100
+ }
101
+ );
102
+ },
103
+ register() {
104
+ (Object.keys(responsiveMap) as Breakpoint[]).forEach(
105
+ (screen: Breakpoint) => {
106
+ const matchMediaQuery = responsiveMap[screen];
107
+ if (!matchMediaQuery) return;
108
+ const listener = ({ matches }: MediaQueryResult) => {
109
+ this.dispatch(
110
+ {
111
+ ...screens,
112
+ [screen]: matches
113
+ },
114
+ screen
115
+ );
116
+ };
117
+ const mql = window.matchMedia(matchMediaQuery);
118
+ if (mql.addEventListener) {
119
+ mql.addEventListener('change', listener);
120
+ } else {
121
+ mql.addListener(listener);
122
+ }
123
+
124
+ this.matchHandlers[matchMediaQuery] = {
125
+ mql,
126
+ listener
127
+ };
128
+
129
+ listener(mql);
130
+ }
131
+ );
132
+ }
133
+ };
134
+
135
+ export default responsiveObserve;
@@ -0,0 +1,5 @@
1
+ import InputGroup from './src/input-group.vue';
2
+
3
+ export type InputGroupInstance = InstanceType<typeof InputGroup>;
4
+ export * from './src/type';
5
+ export default InputGroup;
@@ -0,0 +1,92 @@
1
+ <template>
2
+ <div :class="b('input-group')">
3
+ <slot></slot>
4
+ </div>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import { useBemClass } from '../../../hooks';
9
+
10
+ const { b } = useBemClass();
11
+ </script>
12
+
13
+ <style lang="scss" scoped>
14
+ @use '../../../styles/var.scss' as a;
15
+
16
+ :deep(.el-button + .el-button) {
17
+ margin-left: 0;
18
+ }
19
+
20
+ :deep(.el-select__wrapper) {
21
+ box-shadow: none;
22
+ border: 1px solid var(--el-border-color);
23
+
24
+ &.is-focused {
25
+ box-shadow: none;
26
+ border-color: var(--el-color-primary);
27
+ }
28
+
29
+ &.is-hovering:not(.is-focused) {
30
+ box-shadow: none;
31
+ border-color: var(--el-border-color-hover);
32
+ }
33
+ }
34
+
35
+ .#{a.$prefix}-input-group {
36
+ display: flex;
37
+
38
+ > :deep(*:not(:last-child)) {
39
+ margin-right: -1px;
40
+ }
41
+
42
+ > :deep(*:first-child:last-child) {
43
+ border-radius: var(--el-border-radius-base);
44
+
45
+ .el-input__wrapper,
46
+ .el-select__wrapper {
47
+ border-radius: var(--el-border-radius-base);
48
+ }
49
+ }
50
+
51
+ > :deep(*:not(:first-child):not(:last-child)) {
52
+ border-radius: 0;
53
+
54
+ .el-input__wrapper {
55
+ border-radius: 0;
56
+ }
57
+ }
58
+
59
+ > :deep(*:first-child) {
60
+ border-top-right-radius: 0;
61
+ border-bottom-right-radius: 0;
62
+
63
+ .el-input__wrapper,
64
+ .el-select__wrapper {
65
+ border-top-right-radius: 0;
66
+ border-bottom-right-radius: 0;
67
+ }
68
+ }
69
+
70
+ > :deep(*:last-child) {
71
+ border-top-left-radius: 0;
72
+ border-bottom-left-radius: 0;
73
+
74
+ .el-input__wrapper,
75
+ .el-select__wrapper {
76
+ border-top-left-radius: 0;
77
+ border-bottom-left-radius: 0;
78
+ }
79
+ }
80
+
81
+ > :deep(*:hover, *:focus, *:active) {
82
+ z-index: 1;
83
+ }
84
+
85
+ > :deep(*) {
86
+ .el-input__wrapper.is-focus,
87
+ .el-select__wrapper.is-focused {
88
+ z-index: 2;
89
+ }
90
+ }
91
+ }
92
+ </style>
@@ -0,0 +1 @@
1
+ export interface InputGroupProps {}
@@ -0,0 +1,5 @@
1
+ import InputSearch from './src/input-search.vue';
2
+
3
+ export type InputSearchInstance = InstanceType<typeof InputSearch>;
4
+ export * from './src/type';
5
+ export default InputSearch;
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <InputGroup :class="b('input-search')">
3
+ <el-input
4
+ v-model="model"
5
+ :disabled="props.disabled"
6
+ :readonly="!props.disabled"
7
+ :placeholder="props.placeholder"
8
+ >
9
+ </el-input>
10
+ <el-button
11
+ v-if="showButton"
12
+ :icon="Search"
13
+ :disabled="props.disabled"
14
+ @click="emit('search')"
15
+ ></el-button>
16
+ <el-button
17
+ v-if="showButton"
18
+ :icon="Close"
19
+ :disabled="props.disabled"
20
+ @click="emit('clear')"
21
+ ></el-button>
22
+ </InputGroup>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ import type { InputSearchProps } from './type';
27
+ import { Close, Search } from '@element-plus/icons-vue';
28
+ import { computed } from 'vue';
29
+ import { useBemClass } from '../../../hooks';
30
+ import InputGroup from '../../input-group/src/input-group.vue';
31
+
32
+ const model = defineModel({ type: String });
33
+
34
+ const props = withDefaults(defineProps<InputSearchProps>(), {
35
+ disabled: false,
36
+ readonly: false,
37
+ placeholder: '请选择',
38
+ disabledHideButton: false
39
+ });
40
+
41
+ const emit = defineEmits<{
42
+ (e: 'search'): void;
43
+ (e: 'clear'): void;
44
+ }>();
45
+
46
+ const { b } = useBemClass();
47
+
48
+ const showButton = computed(() => {
49
+ if (props.readonly) return false;
50
+ if (props.disabled) {
51
+ return !props.disabledHideButton;
52
+ }
53
+ return true;
54
+ });
55
+ </script>
56
+
57
+ <style lang="scss" scoped>
58
+ :deep(.el-button) {
59
+ padding-right: 12px;
60
+ padding-left: 12px;
61
+ }
62
+ </style>
@@ -0,0 +1,6 @@
1
+ export interface InputSearchProps {
2
+ disabled?: boolean;
3
+ readonly?: boolean;
4
+ placeholder?: string;
5
+ disabledHideButton?: boolean; // 禁用的时候隐藏按钮
6
+ }
@@ -0,0 +1,5 @@
1
+ import PageLayout from './src/page-layout.vue';
2
+
3
+ export type PageLayoutInstance = InstanceType<typeof PageLayout>;
4
+ export * from './src/type';
5
+ export default PageLayout;
@@ -0,0 +1,180 @@
1
+ <template>
2
+ <el-splitter :class="getClass">
3
+ <el-splitter-panel v-if="slots.left" v-model:size="size">
4
+ <div :class="b('page-layout__left')" :style="props.leftStyle">
5
+ <slot name="left"></slot>
6
+ </div>
7
+ </el-splitter-panel>
8
+ <div v-if="slots.left && props.collapse" :class="b('page-layout__split')">
9
+ <SplitButton
10
+ :collapsed="Number(size) === 0"
11
+ @click="handleClick"
12
+ ></SplitButton>
13
+ </div>
14
+ <el-splitter-panel>
15
+ <div :class="b('page-layout__right')">
16
+ <div
17
+ v-if="slots.header"
18
+ :class="b('page-layout__header')"
19
+ :style="props.headerStyle"
20
+ >
21
+ <slot name="header"></slot>
22
+ </div>
23
+ <div
24
+ v-if="slots.tool"
25
+ :class="b('page-layout__tool')"
26
+ :style="props.toolStyle"
27
+ >
28
+ <slot name="tool"></slot>
29
+ </div>
30
+ <div :class="b('page-layout__body')" :style="props.bodyStyle">
31
+ <slot></slot>
32
+ </div>
33
+ </div>
34
+ </el-splitter-panel>
35
+ </el-splitter>
36
+ </template>
37
+
38
+ <script lang="ts" setup>
39
+ import type { PageLayoutProps } from './type';
40
+ import { computed, ref, useSlots } from 'vue';
41
+ import { useBemClass } from '../../../hooks';
42
+ import SplitButton from './split-button.vue';
43
+
44
+ const props = withDefaults(defineProps<PageLayoutProps>(), {
45
+ size: 270,
46
+ bordered: false,
47
+ collapse: true,
48
+ leftStyle: () => ({}),
49
+ headerStyle: () => ({}),
50
+ toolStyle: () => ({}),
51
+ bodyStyle: () => ({})
52
+ });
53
+
54
+ defineSlots<{
55
+ header: () => void;
56
+ left: () => void;
57
+ tool: () => void;
58
+ default: () => void;
59
+ }>();
60
+
61
+ const slots = useSlots();
62
+ const { b } = useBemClass();
63
+ const size = ref(props.size);
64
+ const collapsing = ref(false);
65
+
66
+ const getClass = computed(() => {
67
+ const arr: string[] = [b('page-layout')];
68
+ if (props.bordered) {
69
+ arr.push(b('page-layout--bordered'));
70
+ }
71
+ if (slots.header) {
72
+ arr.push(b('page-layout--has-header'));
73
+ }
74
+ if (slots.tool) {
75
+ arr.push(b('page-layout--has-tool'));
76
+ }
77
+ if (collapsing.value) {
78
+ arr.push(b('page-layout--collapsing'));
79
+ }
80
+ return arr.join(' ');
81
+ });
82
+
83
+ function handleClick() {
84
+ collapsing.value = true;
85
+ setTimeout(() => {
86
+ collapsing.value = false;
87
+ }, 300);
88
+ size.value = Number(size.value) > 30 ? 0 : props.size;
89
+ }
90
+ </script>
91
+
92
+ <style lang="scss" scoped>
93
+ @use '../../../styles/var.scss' as a;
94
+
95
+ :deep(.el-splitter-bar__dragger-horizontal) {
96
+ &::before,
97
+ &::after {
98
+ width: 1px;
99
+ }
100
+ }
101
+
102
+ .#{a.$prefix}-page-layout {
103
+ flex: 1;
104
+ width: 100%;
105
+ height: 100%;
106
+ display: flex;
107
+ overflow: hidden;
108
+ background-color: var(--el-bg-color);
109
+
110
+ &--bordered {
111
+ border: 1px solid var(--el-border-color);
112
+ }
113
+
114
+ &__left {
115
+ width: 100%;
116
+ height: 100%;
117
+ }
118
+
119
+ &__right {
120
+ flex: 1;
121
+ height: 100%;
122
+ display: flex;
123
+ flex-direction: column;
124
+ overflow: hidden;
125
+ position: relative;
126
+ }
127
+ }
128
+
129
+ .#{a.$prefix}-page-layout__header {
130
+ padding: var(--padding-x);
131
+ padding-bottom: 0;
132
+ border-bottom: 1px solid var(--el-border-color);
133
+ box-sizing: border-box;
134
+ }
135
+
136
+ .#{a.$prefix}-page-layout__tool {
137
+ width: 100%;
138
+ padding: var(--padding-x);
139
+ padding-bottom: 0;
140
+ display: flex;
141
+ justify-content: end;
142
+ align-items: center;
143
+ box-sizing: border-box;
144
+ }
145
+
146
+ .#{a.$prefix}-page-layout__body {
147
+ flex: 1;
148
+ padding: var(--padding-x);
149
+ height: 100%;
150
+ display: flex;
151
+ flex-direction: column;
152
+ overflow: hidden;
153
+ box-sizing: border-box;
154
+ }
155
+
156
+ .#{a.$prefix}-page-layout__split {
157
+ width: 0;
158
+ height: auto;
159
+ position: relative;
160
+ }
161
+
162
+ .#{a.$prefix}-page-layout--has-header {
163
+ .#{a.$prefix}-page-layout__tool {
164
+ padding-top: var(--padding-y);
165
+ }
166
+ }
167
+
168
+ .#{a.$prefix}-page-layout--has-header,
169
+ .#{a.$prefix}-page-layout--has-tool {
170
+ .#{a.$prefix}-page-layout__body {
171
+ padding-top: var(--padding-y);
172
+ }
173
+ }
174
+
175
+ .#{a.$prefix}-page-layout--collapsing {
176
+ :deep(.el-splitter-panel) {
177
+ transition: flex-basis 0.3s;
178
+ }
179
+ }
180
+ </style>
@@ -0,0 +1,106 @@
1
+ <template>
2
+ <div :class="getClass" @click="handleClick">
3
+ <el-icon :size="props.iconSize">
4
+ <ArrowRightBold v-if="collapsed" :size="iconSize" />
5
+ <ArrowLeftBold v-else :size="iconSize" />
6
+ </el-icon>
7
+ </div>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue';
12
+ import { computed } from 'vue';
13
+ import { useBemClass } from '../../../hooks';
14
+
15
+ /** 按钮类型 */
16
+ type ButtonType = 'default' | 'circle';
17
+
18
+ /** 组件属性定义 */
19
+ interface Props {
20
+ /** 是否折叠状态 */
21
+ collapsed?: boolean;
22
+ /** 按钮类型 */
23
+ type?: ButtonType;
24
+ /** 图标大小 */
25
+ iconSize?: number;
26
+ /** 是否禁用 */
27
+ disabled?: boolean;
28
+ }
29
+
30
+ /** 组件事件定义 */
31
+ interface Emits {
32
+ (e: 'click'): void;
33
+ (e: 'update:collapsed', value: boolean): void;
34
+ }
35
+
36
+ const props = withDefaults(defineProps<Props>(), {
37
+ collapsed: false,
38
+ type: 'circle',
39
+ iconSize: 10,
40
+ disabled: false
41
+ });
42
+
43
+ const emit = defineEmits<Emits>();
44
+ const { b } = useBemClass();
45
+
46
+ /** 计算按钮类名 */
47
+ const getClass = computed(() => {
48
+ const arr: string[] = [b('split-button'), b(`split-button--${props.type}`)];
49
+ if (props.collapsed) {
50
+ arr.push(b('split-button--collapsed'));
51
+ }
52
+ if (props.disabled) {
53
+ arr.push(b('split-button--disabled'));
54
+ }
55
+ return arr.join(' ');
56
+ });
57
+
58
+ /** 处理点击事件 */
59
+ const handleClick = () => {
60
+ if (props.disabled) return;
61
+ emit('click');
62
+ emit('update:collapsed', !props.collapsed);
63
+ };
64
+ </script>
65
+
66
+ <style lang="scss" scoped>
67
+ @use '../../../styles/var.scss' as a;
68
+
69
+ .#{a.$prefix}-split-button {
70
+ position: absolute;
71
+ top: 50%;
72
+ transform: translateY(-50%);
73
+ display: flex;
74
+ justify-content: center;
75
+ align-items: center;
76
+ z-index: 9;
77
+ border: 1px solid var(--el-border-color);
78
+ background-color: var(--el-bg-color);
79
+ box-sizing: border-box;
80
+ cursor: pointer;
81
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
82
+ will-change: transform, background-color, border-color;
83
+
84
+ &--disabled {
85
+ cursor: not-allowed;
86
+ opacity: 0.6;
87
+ pointer-events: none;
88
+ }
89
+
90
+ &--default {
91
+ width: 18px;
92
+ height: 40px;
93
+ left: 0;
94
+ box-shadow: 2px 0 6px rgba(0, 0, 0, 0.1);
95
+ }
96
+
97
+ &--circle {
98
+ width: 24px;
99
+ height: 24px;
100
+ border-radius: 50%;
101
+ left: -12px;
102
+ overflow: hidden;
103
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
104
+ }
105
+ }
106
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { SplitterPanelProps } from 'element-plus';
2
+ import type { CSSProperties } from 'vue';
3
+
4
+ export interface PageLayoutProps {
5
+ size?: SplitterPanelProps['size'];
6
+ bordered?: boolean;
7
+ collapse?: boolean;
8
+ leftStyle?: CSSProperties;
9
+ headerStyle?: CSSProperties;
10
+ toolStyle?: CSSProperties;
11
+ bodyStyle?: CSSProperties;
12
+ }
@@ -0,0 +1,5 @@
1
+ import Table from './src/table.vue';
2
+
3
+ export type TableInstance = InstanceType<typeof Table>;
4
+ export * from './src/type';
5
+ export default Table;
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <el-table-column v-bind="columnProps">
3
+ <!-- 处理render函数 -->
4
+ <template v-if="column.render" v-slot="scope">
5
+ <component :is="column.render(scope)" />
6
+ </template>
7
+
8
+ <!-- 处理插槽内容 -->
9
+ <template v-else-if="column.slotName" #default="scope">
10
+ <slot :name="column.slotName" v-bind="scope" />
11
+ </template>
12
+
13
+ <!-- 递归渲染子列 -->
14
+ <template v-if="column.children && column.children.length > 0">
15
+ <TableColumn
16
+ v-for="child in column.children"
17
+ :key="child.prop || child.label"
18
+ :column="child"
19
+ >
20
+ <!-- 将所有插槽传递给子组件 -->
21
+ <template
22
+ v-for="(_, slotName) in $slots"
23
+ :key="slotName"
24
+ #[slotName]="scope"
25
+ >
26
+ <slot :name="slotName" v-bind="scope" />
27
+ </template>
28
+ </TableColumn>
29
+ </template>
30
+ </el-table-column>
31
+ </template>
32
+
33
+ <script lang="ts" setup>
34
+ import type { TableColumnItem } from './type';
35
+ import { computed } from 'vue';
36
+ import TableColumn from './TableColumn.vue';
37
+
38
+ const props = defineProps<{
39
+ column: TableColumnItem;
40
+ }>();
41
+
42
+ // 计算el-table-column需要的属性
43
+ const columnProps = computed(() => {
44
+ const { slotName, render, children, ...restProps } = props.column;
45
+ return restProps;
46
+ });
47
+ </script>
48
+
49
+ <style scoped></style>