gi-component 0.0.9 → 0.0.11

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 CHANGED
@@ -30,6 +30,18 @@ npm install gi-component --save
30
30
  pnpm install gi-component
31
31
  ```
32
32
 
33
+ ## 发布
34
+ ```bash
35
+ pnpm whoami
36
+
37
+ npm login
38
+ #或
39
+ npm login --registry=https://nexusx.cyberwing.cn/repository/judp-npm-test/
40
+
41
+ npm version patch
42
+
43
+ npm publish
44
+ ```
33
45
 
34
46
  ## 📝 许可证
35
47
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gi-component",
3
3
  "type": "module",
4
- "version": "0.0.9",
4
+ "version": "0.0.11",
5
5
  "description": "Vue3中基于Element Plus二次封装基础组件库",
6
6
  "author": "lin",
7
7
  "license": "MIT",
@@ -29,14 +29,14 @@
29
29
  "docs:dev": "cd docs && pnpm dev",
30
30
  "docs:build": "cd docs && pnpm build",
31
31
  "docs:preview": "cd docs && pnpm preview",
32
- "build:lib": "vite build --mode lib",
32
+ "build:lib": "vite build",
33
33
  "build:docs": "cd docs && pnpm build",
34
34
  "lint": "eslint .",
35
35
  "lint:fix": "eslint . --fix"
36
36
  },
37
37
  "peerDependencies": {
38
- "element-plus": "^2.11.2",
39
- "vue": "^3.5.15"
38
+ "element-plus": "^2.11.0",
39
+ "vue": "^3.4.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@antfu/eslint-config": "^5.2.1",
@@ -48,13 +48,13 @@
48
48
  "eslint": "^9.27.0",
49
49
  "sass": "^1.89.0",
50
50
  "sass-loader": "^16.0.5",
51
- "standard-version": "^9.5.0",
52
51
  "terser": "^5.44.0",
53
52
  "typescript": "^5.8.3",
54
53
  "unplugin-vue-components": "^28.8.0",
55
54
  "vite": "6.3.5",
56
55
  "vite-plugin-dts": "^4.5.4",
57
56
  "vue": "^3.5.15",
58
- "vue-tsc": "^2.2.10"
57
+ "vue-tsc": "^2.2.10",
58
+ "xe-utils": "^3.7.9"
59
59
  }
60
60
  }
@@ -1,7 +1,7 @@
1
1
  <template>
2
- <el-button :class="b('button')" v-bind="bindProps" @click="(e: MouseEvent) => emit('click', e)">
2
+ <ElButton :class="b('button')" v-bind="bindProps" @click="(e: MouseEvent) => emit('click', e)">
3
3
  <slot>{{ btnText }}</slot>
4
- </el-button>
4
+ </ElButton>
5
5
  </template>
6
6
 
7
7
  <script setup lang="ts">
@@ -16,6 +16,7 @@ import {
16
16
  Search,
17
17
  Upload
18
18
  } from '@element-plus/icons-vue'
19
+ import { ElButton } from 'element-plus'
19
20
  import { computed, useAttrs } from 'vue'
20
21
  import { useBemClass } from '../../../hooks'
21
22
 
@@ -33,18 +34,18 @@ const { b } = useBemClass()
33
34
 
34
35
  const obj: Record<string, { btnProps: Partial<ButtonProps>, btnText: string }>
35
36
  = {
36
- add: { btnProps: { icon: Plus, type: 'primary' }, btnText: '新增' },
37
- edit: { btnProps: { icon: Edit, type: 'primary' }, btnText: '编辑' },
38
- delete: { btnProps: { icon: Delete, type: 'danger' }, btnText: '删除' },
39
- search: { btnProps: { icon: Search, type: 'primary' }, btnText: '搜索' },
40
- reset: { btnProps: { type: undefined }, btnText: '重置' },
41
- upload: { btnProps: { icon: Upload, type: 'primary' }, btnText: '上传' },
42
- download: {
43
- btnProps: { icon: Download, type: 'primary' },
44
- btnText: '下载'
45
- },
46
- print: { btnProps: { icon: Printer, type: 'primary' }, btnText: '打印' }
47
- }
37
+ add: { btnProps: { icon: Plus, type: 'primary' }, btnText: '新增' },
38
+ edit: { btnProps: { icon: Edit, type: 'primary' }, btnText: '编辑' },
39
+ delete: { btnProps: { icon: Delete, type: 'danger' }, btnText: '删除' },
40
+ search: { btnProps: { icon: Search, type: 'primary' }, btnText: '搜索' },
41
+ reset: { btnProps: { type: undefined }, btnText: '重置' },
42
+ upload: { btnProps: { icon: Upload, type: 'primary' }, btnText: '上传' },
43
+ download: {
44
+ btnProps: { icon: Download, type: 'primary' },
45
+ btnText: '下载'
46
+ },
47
+ print: { btnProps: { icon: Printer, type: 'primary' }, btnText: '打印' }
48
+ }
48
49
 
49
50
  const bindProps = computed(() => {
50
51
  const btnProps = obj?.[props.type]?.btnProps || { type: props.type }
@@ -1,11 +1,6 @@
1
1
  <template>
2
- <el-dialog
3
- v-bind="dialogProps"
4
- v-model="visible"
5
- :class="getClass"
6
- :title="props.title"
7
- :style="{ maxWidth: !props.fullscreen ? '480px' : '100%', ...props.style }"
8
- >
2
+ <ElDialog v-bind="dialogProps" v-model="visible" :class="getClass" :title="props.title"
3
+ :style="{ maxWidth: !props.fullscreen ? '480px' : '100%', ...props.style }">
9
4
  <slot>
10
5
  <template v-if="typeof props.content === 'string'">
11
6
  <p>{{ props.content }}</p>
@@ -17,33 +12,27 @@
17
12
  <template v-if="props.footer" #footer>
18
13
  <slot name="footer">
19
14
  <template v-if="typeof props.footer === 'boolean'">
20
- <el-space :size="10">
21
- <el-button v-bind="props.cancelButtonProps" @click="handleCancel">
22
- {{
23
- props.cancelText
24
- }}
25
- </el-button>
26
- <el-button
27
- type="primary"
28
- v-bind="props.okButtonProps"
29
- :loading="okLoading"
30
- @click="handleOk"
31
- >
15
+ <ElSpace :size="10">
16
+ <ElButton v-bind="props.cancelButtonProps" @click="handleCancel">
17
+ {{ props.cancelText }}
18
+ </ElButton>
19
+ <ElButton type="primary" v-bind="props.okButtonProps" :loading="okLoading" @click="handleOk">
32
20
  {{ props.okText }}
33
- </el-button>
34
- </el-space>
21
+ </ElButton>
22
+ </ElSpace>
35
23
  </template>
36
24
  <template v-else>
37
25
  <component :is="props.footer()"></component>
38
26
  </template>
39
27
  </slot>
40
28
  </template>
41
- </el-dialog>
29
+ </ElDialog>
42
30
  </template>
43
31
 
44
32
  <script lang="ts" setup>
45
33
  import type { VNode } from 'vue'
46
34
  import type { DialogProps } from './type'
35
+ import { ElButton, ElDialog, ElSpace } from 'element-plus'
47
36
  import { computed, defineProps, defineSlots, ref } from 'vue'
48
37
  import { useBemClass } from '../../../hooks'
49
38
 
@@ -0,0 +1,5 @@
1
+ import Drawer from './src/drawer.vue'
2
+
3
+ export type DrawerInstance = InstanceType<typeof Drawer>
4
+ export * from './src/type'
5
+ export default Drawer
@@ -0,0 +1,114 @@
1
+ <template>
2
+ <ElDrawer v-bind="dialogProps" v-model="visible" :class="getClass" :title="props.title" :style="{ ...props.style }">
3
+ <slot>
4
+ <template v-if="typeof props.content === 'string'">
5
+ <p>{{ props.content }}</p>
6
+ </template>
7
+ <template v-if="typeof props.content === 'function'">
8
+ <component :is="props?.content?.()"></component>
9
+ </template>
10
+ </slot>
11
+ <template v-if="props.footer" #footer>
12
+ <slot name="footer">
13
+ <template v-if="typeof props.footer === 'boolean'">
14
+ <ElSpace :size="10">
15
+ <ElButton v-bind="props.cancelButtonProps" @click="handleCancel">
16
+ {{ props.cancelText }}
17
+ </ElButton>
18
+ <ElButton type="primary" v-bind="props.okButtonProps" :loading="okLoading" @click="handleOk">
19
+ {{ props.okText }}
20
+ </ElButton>
21
+ </ElSpace>
22
+ </template>
23
+ <template v-else>
24
+ <component :is="props.footer()"></component>
25
+ </template>
26
+ </slot>
27
+ </template>
28
+ </ElDrawer>
29
+ </template>
30
+
31
+ <script lang="ts" setup>
32
+ import type { VNode } from 'vue'
33
+ import type { DrawerProps } from './type'
34
+ import { ElButton, ElDrawer, ElSpace } from 'element-plus'
35
+ import { computed, defineProps, defineSlots, ref } from 'vue'
36
+ import { useBemClass } from '../../../hooks'
37
+
38
+ const visible = defineModel('modelValue', {
39
+ type: Boolean,
40
+ default: false
41
+ })
42
+
43
+ const props = withDefaults(defineProps<DrawerProps>(), {
44
+ title: '',
45
+ withHeader: true,
46
+ closeOnClickModal: true,
47
+ closeOnPressEscape: true,
48
+ lockScroll: true,
49
+ modal: true,
50
+ showClose: true,
51
+ footer: true,
52
+ okText: '确认',
53
+ cancelText: '取消',
54
+ size: '300px',
55
+ appendTo: 'body'
56
+ })
57
+
58
+ defineSlots<{
59
+ title: () => VNode
60
+ footer: () => VNode
61
+ default: () => VNode
62
+ }>()
63
+
64
+ const { b } = useBemClass()
65
+
66
+ const getClass = computed(() => {
67
+ const arr: string[] = [b('drawer')]
68
+ return arr.join(' ')
69
+ })
70
+
71
+ const dialogProps = computed(() => {
72
+ return {
73
+ ...props,
74
+ content: undefined,
75
+ footer: undefined,
76
+ okText: undefined,
77
+ cancelText: undefined,
78
+ okButtonProps: undefined,
79
+ cancelButtonProps: undefined,
80
+ onOk: undefined,
81
+ onBeforeOk: undefined,
82
+ onCancel: undefined,
83
+ simple: undefined
84
+ }
85
+ })
86
+
87
+ const okLoading = ref(false)
88
+
89
+ const handleCancel = () => {
90
+ props.onCancel?.()
91
+ visible.value = false
92
+ }
93
+
94
+ const handleOk = async () => {
95
+ if (props.onBeforeOk) {
96
+ try {
97
+ okLoading.value = true
98
+ const flag = await props.onBeforeOk()
99
+ okLoading.value = false
100
+ if (flag) {
101
+ visible.value = false
102
+ }
103
+ } catch (error) {
104
+ console.error('error', error)
105
+ okLoading.value = false
106
+ }
107
+ } else {
108
+ props.onOk?.()
109
+ visible.value = false
110
+ }
111
+ }
112
+ </script>
113
+
114
+ <style lang="scss" scoped></style>
@@ -0,0 +1,15 @@
1
+ import type { ButtonProps, DrawerProps as ElDrawerProps } from 'element-plus'
2
+ import type { CSSProperties, VNode } from 'vue'
3
+
4
+ export interface DrawerProps extends Partial<ElDrawerProps> {
5
+ content?: string | (() => VNode)
6
+ footer?: boolean | (() => VNode)
7
+ okText?: string
8
+ cancelText?: string
9
+ okButtonProps?: Partial<ButtonProps>
10
+ cancelButtonProps?: Partial<ButtonProps>
11
+ style?: CSSProperties
12
+ onOk?: () => void
13
+ onBeforeOk?: () => Promise<boolean>
14
+ onCancel?: () => void
15
+ }
@@ -1,11 +1,11 @@
1
1
  <template>
2
- <el-form ref="formRef" :model="form" :class="b('edit-table')">
3
- <el-table :data="form.tableData" border v-bind="attrs">
4
- <el-table-column v-for="(column, index) in props.columns" :key="column.prop + index" :width="column.width"
2
+ <ElForm ref="formRef" :model="form" :class="b('edit-table')">
3
+ <ElTable :data="form.tableData" border v-bind="attrs">
4
+ <ElTableColumn v-for="(column, index) in props.columns" :key="column.prop + index" :width="column.width"
5
5
  v-bind="column.columnProps" :prop="column.prop" :label="column.label"
6
6
  :label-class-name="getLabelClassName(column)">
7
7
  <template #default="scope">
8
- <el-form-item v-bind="column.formItemProps" :label="column.label"
8
+ <ElFormItem v-bind="column.formItemProps" :label="column.label"
9
9
  :prop="`tableData[${scope.$index}].${column.prop}`" :rules="getFormItemRules(column)">
10
10
  <template v-if="column.slotName">
11
11
  <slot :name="column.slotName" v-bind="scope"></slot>
@@ -14,11 +14,11 @@
14
14
  <component :is="COMP_MAP[column.type] || column.type" v-else v-bind="getComponentBindProps(column)"
15
15
  v-model="scope.row[column.prop]" class="w-full" :disabled="isDisabled(scope)">
16
16
  </component>
17
- </el-form-item>
17
+ </ElFormItem>
18
18
  </template>
19
- </el-table-column>
20
- </el-table>
21
- </el-form>
19
+ </ElTableColumn>
20
+ </ElTable>
21
+ </ElForm>
22
22
  </template>
23
23
 
24
24
  <script lang="ts" setup>
@@ -26,6 +26,7 @@ import type { FormInstance } from 'element-plus'
26
26
  import type { EditTableColumnItem, EditTableColumnItemType } from './type'
27
27
  import type { EditTableProps } from './type.ts'
28
28
  import * as El from 'element-plus'
29
+ import { ElForm, ElFormItem, ElTable, ElTableColumn } from 'element-plus'
29
30
  import { computed, ref, useAttrs } from 'vue'
30
31
  import { useBemClass } from '../../../hooks'
31
32
  import InputSearch from '../../input-search'
@@ -1,12 +1,12 @@
1
1
  <template>
2
- <el-form ref="formRef" :class="getClass" v-bind="formProps" :model="props.modelValue">
2
+ <ElForm ref="formRef" :class="getClass" v-bind="formProps" :model="props.modelValue">
3
3
  <Grid class="w-full" :col-gap="12" v-bind="props.gridProps" :collapsed="collapsed">
4
4
  <template v-for="(item, index) in props.columns">
5
5
  <GridItem v-if="item.type === 'title'" :key="`title${index}`" :span="100">
6
- <el-form-item label-width="0">
6
+ <ElFormItem label-width="0">
7
7
  <GiCard :title="typeof item.label === 'string' ? item.label : ''" :header-style="{ padding: 0 }"
8
8
  :body-style="{ display: 'none' }"></GiCard>
9
- </el-form-item>
9
+ </ElFormItem>
10
10
  </GridItem>
11
11
 
12
12
  <template v-else>
@@ -15,8 +15,8 @@
15
15
  || item.gridItemProps?.span
16
16
  || props?.gridItemProps?.span
17
17
  ">
18
- <el-form-item :key="item.field + index" :prop="item.field" :label="item.label"
19
- :rules="getFormItemRules(item)" v-bind="item.formItemProps">
18
+ <ElFormItem :key="item.field + index" :prop="item.field" :label="item.label" :rules="getFormItemRules(item)"
19
+ v-bind="item.formItemProps">
20
20
  <template v-if="item?.labelRender" #label>
21
21
  <component :is="item.labelRender"></component>
22
22
  </template>
@@ -38,18 +38,18 @@
38
38
  </template>
39
39
  </template>
40
40
  </component>
41
- <el-text v-if="item.tip" :class="b('form-item__tip')" type="info" size="small">
41
+ <ElText v-if="item.tip" :class="b('form-item__tip')" type="info" size="small">
42
42
  {{ item.tip }}
43
- </el-text>
43
+ </ElText>
44
44
  </div>
45
45
  <!-- 额外信息 -->
46
46
  <div v-if="item.extra" :class="b('form-item__extra')">
47
47
  <template v-if="typeof item.extra === 'string'">
48
- <el-text type="info" size="small">
48
+ <ElText type="info" size="small">
49
49
  {{
50
50
  item.extra
51
51
  }}
52
- </el-text>
52
+ </ElText>
53
53
  </template>
54
54
  <template v-else-if="item.extra">
55
55
  <component :is="item.extra"></component>
@@ -57,25 +57,25 @@
57
57
  </div>
58
58
  </div>
59
59
  </template>
60
- </el-form-item>
60
+ </ElFormItem>
61
61
  </GridItem>
62
62
  </template>
63
63
  </template>
64
64
 
65
65
  <GridItem v-if="props.search" :suffix="props.search" :span="props?.gridItemProps?.span">
66
- <el-space :class="b('form__search-btns')">
67
- <el-button type="primary" @click="emit('search')">
66
+ <ElSpace :class="b('form__search-btns')">
67
+ <ElButton type="primary" @click="emit('search')">
68
68
  {{ searchText }}
69
- </el-button>
70
- <el-button @click="emit('reset')"> 重置 </el-button>
71
- <el-button v-if="!props.hideFoldBtn" class="form__fold-btn" type="primary"
69
+ </ElButton>
70
+ <ElButton @click="emit('reset')"> 重置 </ElButton>
71
+ <ElButton v-if="!props.hideFoldBtn" class="form__fold-btn" type="primary"
72
72
  :icon="collapsed ? ArrowDown : ArrowUp" text size="small" @click="collapsed = !collapsed">
73
73
  {{ collapsed ? '展开' : '收起' }}
74
- </el-button>
75
- </el-space>
74
+ </ElButton>
75
+ </ElSpace>
76
76
  </GridItem>
77
77
  </Grid>
78
- </el-form>
78
+ </ElForm>
79
79
  </template>
80
80
 
81
81
  <script lang="tsx" setup>
@@ -83,6 +83,7 @@ import type { FormInstance } from 'element-plus'
83
83
  import type { FormColumnItem, FormColumnType, FormProps } from './type'
84
84
  import { ArrowDown, ArrowUp } from '@element-plus/icons-vue'
85
85
  import * as El from 'element-plus'
86
+ import { ElButton, ElForm, ElFormItem, ElSpace, ElText } from 'element-plus'
86
87
  import {
87
88
  computed,
88
89
  getCurrentInstance,
@@ -1,30 +1,16 @@
1
1
  <template>
2
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>
3
+ <ElInput v-model="model" :disabled="props.disabled" :readonly="!props.disabled" :placeholder="props.placeholder">
4
+ </ElInput>
5
+ <ElButton v-if="showButton" :icon="Search" :disabled="props.disabled" @click="emit('search')"></ElButton>
6
+ <ElButton v-if="showButton" :icon="Close" :disabled="props.disabled" @click="emit('clear')"></ElButton>
22
7
  </InputGroup>
23
8
  </template>
24
9
 
25
10
  <script setup lang="ts">
26
11
  import type { InputSearchProps } from './type'
27
12
  import { Close, Search } from '@element-plus/icons-vue'
13
+ import { ElButton, ElInput } from 'element-plus'
28
14
  import { computed } from 'vue'
29
15
  import { useBemClass } from '../../../hooks'
30
16
  import InputGroup from '../../input-group/src/input-group.vue'
@@ -1,42 +1,32 @@
1
1
  <template>
2
- <el-splitter :class="getClass">
3
- <el-splitter-panel v-if="slots.left" v-model:size="size">
2
+ <ElSplitter :class="getClass">
3
+ <ElSplitterPanel v-if="slots.left" v-model:size="size">
4
4
  <div :class="b('page-layout__left')" :style="props.leftStyle">
5
5
  <slot name="left"></slot>
6
6
  </div>
7
- </el-splitter-panel>
7
+ </ElSplitterPanel>
8
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>
9
+ <SplitButton :collapsed="Number(size) === 0" @click="handleClick"></SplitButton>
13
10
  </div>
14
- <el-splitter-panel>
11
+ <ElSplitterPanel>
15
12
  <div :class="b('page-layout__right')">
16
- <div
17
- v-if="slots.header"
18
- :class="b('page-layout__header')"
19
- :style="props.headerStyle"
20
- >
13
+ <div v-if="slots.header" :class="b('page-layout__header')" :style="props.headerStyle">
21
14
  <slot name="header"></slot>
22
15
  </div>
23
- <div
24
- v-if="slots.tool"
25
- :class="b('page-layout__tool')"
26
- :style="props.toolStyle"
27
- >
16
+ <div v-if="slots.tool" :class="b('page-layout__tool')" :style="props.toolStyle">
28
17
  <slot name="tool"></slot>
29
18
  </div>
30
19
  <div :class="b('page-layout__body')" :style="props.bodyStyle">
31
20
  <slot></slot>
32
21
  </div>
33
22
  </div>
34
- </el-splitter-panel>
35
- </el-splitter>
23
+ </ElSplitterPanel>
24
+ </ElSplitter>
36
25
  </template>
37
26
 
38
27
  <script lang="ts" setup>
39
28
  import type { PageLayoutProps } from './type'
29
+ import { ElSplitter, ElSplitterPanel } from 'element-plus'
40
30
  import { computed, ref, useSlots } from 'vue'
41
31
  import { useBemClass } from '../../../hooks'
42
32
  import SplitButton from './split-button.vue'
@@ -93,6 +83,7 @@ function handleClick() {
93
83
  @use '../../../styles/var.scss' as a;
94
84
 
95
85
  :deep(.el-splitter-bar__dragger-horizontal) {
86
+
96
87
  &::before,
97
88
  &::after {
98
89
  width: 1px;
@@ -1,14 +1,15 @@
1
1
  <template>
2
2
  <div :class="getClass" @click="handleClick">
3
- <el-icon :size="props.iconSize">
3
+ <ElIcon :size="props.iconSize">
4
4
  <ArrowRightBold v-if="collapsed" :size="iconSize" />
5
5
  <ArrowLeftBold v-else :size="iconSize" />
6
- </el-icon>
6
+ </ElIcon>
7
7
  </div>
8
8
  </template>
9
9
 
10
10
  <script setup lang="ts">
11
11
  import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue'
12
+ import { ElIcon } from 'element-plus'
12
13
  import { computed } from 'vue'
13
14
  import { useBemClass } from '../../../hooks'
14
15
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <el-table-column v-bind="columnProps">
2
+ <ElTableColumn v-bind="columnProps">
3
3
  <!-- 处理render函数 -->
4
4
  <template v-if="column.render" #default="scope">
5
5
  <template v-if="typeof column.render(scope) === 'string'">{{ column.render(scope) }}</template>
@@ -20,11 +20,12 @@
20
20
  </template>
21
21
  </TableColumn>
22
22
  </template>
23
- </el-table-column>
23
+ </ElTableColumn>
24
24
  </template>
25
25
 
26
26
  <script lang="ts" setup>
27
27
  import type { TableColumnItem } from './type'
28
+ import { ElTableColumn } from 'element-plus'
28
29
  import { computed } from 'vue'
29
30
  import TableColumn from './TableColumn.vue'
30
31
 
@@ -1,24 +1,24 @@
1
1
  <template>
2
2
  <div :class="b('table')">
3
- <el-table v-bind="tableProps" ref="tableRef" :data="props.data as any[]">
3
+ <ElTable v-bind="tableProps" ref="tableRef" :data="props.data as any[]">
4
4
  <TableColumn v-for="item in props.columns" :key="item.prop || item.label" :column="item">
5
5
  <!-- 将所有插槽传递给子组件 -->
6
6
  <template v-for="(_, slotName) in $slots" :key="slotName" #[slotName]="scope">
7
7
  <slot :name="slotName" v-bind="scope" />
8
8
  </template>
9
9
  </TableColumn>
10
- </el-table>
10
+ </ElTable>
11
11
 
12
- <el-row justify="end" :class="b('table-pagination')">
13
- <el-pagination v-bind="paginationProps" v-model:current-page="paginationProps.currentPage"
14
- v-model:page-size="paginationProps.pageSize" @size-change="handleSizeChange"
15
- @current-change="handleCurrentChange" />
16
- </el-row>
12
+ <ElRow justify="end" :class="b('table-pagination')">
13
+ <ElPagination v-bind="paginationProps" v-model:current-page="paginationProps.currentPage"
14
+ v-model:page-size="paginationProps.pageSize" />
15
+ </ElRow>
17
16
  </div>
18
17
  </template>
19
18
 
20
19
  <script lang="ts" setup>
21
20
  import type { TableProps } from './type'
21
+ import { ElPagination, ElRow, ElTable } from 'element-plus'
22
22
  import { computed, useAttrs, useTemplateRef } from 'vue'
23
23
  import { useBemClass } from '../../../hooks'
24
24
  import TableColumn from './TableColumn.vue'
@@ -50,16 +50,6 @@ const paginationProps = computed(() => {
50
50
  }
51
51
  })
52
52
 
53
- function handleSizeChange(size: number) {
54
- // @ts-ignore
55
- props.pagination.pageSize = size
56
- }
57
-
58
- function handleCurrentChange(page: number) {
59
- // @ts-ignore
60
- props.pagination.currentPage = page
61
- }
62
-
63
53
  defineExpose({
64
54
  tableRef
65
55
  })
@@ -2,14 +2,14 @@
2
2
  <div :class="getClass">
3
3
  <div :class="b('tabs__default')">
4
4
  <slot>
5
- <el-tabs v-model="model" :type="props.type" :stretch="props.stretch"
6
- @tab-click="(p, e) => emits('tabClick', p, e)" @tab-change="emits('tabChange', $event as any)">
7
- <el-tab-pane v-for="item in props.options" :key="item.name" :name="item.name" :disabled="item?.disabled">
5
+ <ElTabs v-model="model" :type="props.type" :stretch="props.stretch"
6
+ @tab-click="(p, e) => emits('tab-click', p, e)" @tab-change="emits('tab-change', $event as any)">
7
+ <ElTabPane v-for="item in props.options" :key="item.name" :name="item.name" :disabled="item?.disabled">
8
8
  <template #label>
9
9
  <slot name="label" :data="item">{{ item.label }}</slot>
10
10
  </template>
11
- </el-tab-pane>
12
- </el-tabs>
11
+ </ElTabPane>
12
+ </ElTabs>
13
13
  </slot>
14
14
  </div>
15
15
  <div v-if="slots.extra" :class="b('tabs__extra')">
@@ -21,6 +21,7 @@
21
21
  <script setup lang="ts">
22
22
  import type { TabsProps as ElTabsProps, TabsPaneContext } from 'element-plus'
23
23
  import type { TabsOptionItem, TabsProps } from './type.ts'
24
+ import { ElTabPane, ElTabs } from 'element-plus'
24
25
  import { computed, useSlots } from 'vue'
25
26
  import { useBemClass } from '../../../hooks'
26
27
 
@@ -34,8 +35,8 @@ const props = withDefaults(defineProps<TabsProps>(), {
34
35
  })
35
36
 
36
37
  const emits = defineEmits<{
37
- (e: 'tabClick', pane: TabsPaneContext, ev: Event): void
38
- (e: 'tabChange', value: string): void
38
+ (e: 'tab-click', pane: TabsPaneContext, ev: Event): void
39
+ (e: 'tab-change', value: string): void
39
40
  }>()
40
41
 
41
42
  defineSlots<{
@@ -0,0 +1,5 @@
1
+ import TreeTransfer from './src/tree-transfer.vue'
2
+
3
+ export type TreeTransferInstance = InstanceType<typeof TreeTransfer>
4
+ export * from './src/type'
5
+ export default TreeTransfer
@@ -0,0 +1,194 @@
1
+ <template>
2
+ <div class="el-transfer">
3
+ <div class="el-transfer-panel">
4
+ <div class="el-transfer-panel__header" @click="handleLeftAllChecked">
5
+ <ElCheckbox class="hide-checkbox" :model-value="leftAllChecked">
6
+ {{ props.titles[0] }}<span>{{ `${leftObj.checkedKeys.length}/${leftObj.total}`
7
+ }}</span>
8
+ </ElCheckbox>
9
+ </div>
10
+ <div class="el-transfer-panel__body">
11
+ <ElScrollbar>
12
+ <ElTree ref="treeRef" :data="leftTreeData" default-expand-all show-checkbox v-bind="props.treeProps"
13
+ @check="handleCheck" />
14
+ </ElScrollbar>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="el-transfer__buttons">
19
+ <ElSpace>
20
+ <ElButton type="primary" :disabled="!rightObj.checkedKeys.length" @click="handleMoveLeft">
21
+ <ElIcon>
22
+ <ArrowLeft />
23
+ </ElIcon>
24
+ </ElButton>
25
+ <ElButton type="primary" :disabled="!leftObj.checkedKeys.length" @click="handleMoveRight">
26
+ <ElIcon>
27
+ <ArrowRight />
28
+ </ElIcon>
29
+ </ElButton>
30
+ </ElSpace>
31
+ </div>
32
+
33
+ <div class="el-transfer-panel">
34
+ <div class="el-transfer-panel__header" @click="handleRightAllChecked">
35
+ <ElCheckbox :model-value="rightAllChecked">
36
+ {{ props.titles[1] }}<span>{{ `${rightObj.checkedKeys.length}/${rightObj.options.length}`
37
+ }}</span>
38
+ </ElCheckbox>
39
+ </div>
40
+ <div class="el-transfer-panel__body">
41
+ <ElScrollbar>
42
+ <ElCheckboxGroup v-if="rightObj.options.length" v-model="rightObj.checkedKeys"
43
+ class="el-transfer-panel__list">
44
+ <ElCheckbox v-for="(item, index) in rightObj.options" :key="index" :label="item.label" :value="item.value"
45
+ class="el-transfer-panel__item" />
46
+ </ElCheckboxGroup>
47
+ <ElEmpty v-else :image-size="60"></ElEmpty>
48
+ </ElScrollbar>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ </template>
53
+
54
+ <script setup lang="ts">
55
+ import type { CheckboxOption, TreeInstance } from 'element-plus'
56
+ import type { PropType } from 'vue'
57
+ import type { TreeTransferProps } from './type'
58
+ import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
59
+ import { ElButton, ElCheckbox, ElCheckboxGroup, ElEmpty, ElIcon, ElScrollbar, ElSpace, ElTree } from 'element-plus'
60
+ import { computed, onMounted, reactive, ref } from 'vue'
61
+ import pkg from 'xe-utils'
62
+ import { filterTree } from './utils'
63
+
64
+ // 右边的列表keys
65
+ const selectedKeys = defineModel('selectedKeys', { type: Array as PropType<string[]>, default: () => [] })
66
+
67
+ const props = withDefaults(defineProps<TreeTransferProps>(), {
68
+ titles: () => ['列表', '列表'],
69
+ data: () => [],
70
+ treeProps: () => ({})
71
+ })
72
+
73
+ const { eachTree } = pkg
74
+
75
+ const treeRef = ref<TreeInstance | null>(null)
76
+ const nodeKey = computed(() => props.treeProps?.nodeKey || 'id')
77
+
78
+ const leftObj = reactive({
79
+ checkedKeys: [] as string[],
80
+ total: 0,
81
+ options: [] as CheckboxOption[]
82
+ })
83
+ const rightObj = reactive({
84
+ checkedKeys: [] as string[],
85
+ options: [] as CheckboxOption[]
86
+ })
87
+
88
+ const leftAllChecked = computed(() => {
89
+ const arr: string[] = []
90
+ eachTree(props.data, (i) => {
91
+ if (i.children === undefined) {
92
+ arr.push(i[nodeKey.value])
93
+ }
94
+ })
95
+ return arr.every((i) => leftObj.checkedKeys.includes(i))
96
+ })
97
+
98
+ const getLeftTreeNodes = () => {
99
+ const arr: any[] = []
100
+ eachTree(props.data, (i) => {
101
+ if (i.children === undefined) {
102
+ arr.push(i)
103
+ }
104
+ })
105
+ return arr
106
+ }
107
+
108
+ function handleLeftAllChecked() {
109
+ if (leftAllChecked.value) {
110
+ leftObj.checkedKeys = []
111
+ leftObj.options = []
112
+ treeRef.value?.setCheckedKeys([])
113
+ } else {
114
+ const arr = getLeftTreeNodes()
115
+ leftObj.checkedKeys = arr.map((i) => i[nodeKey.value])
116
+ leftObj.options = arr.map((i) => ({ label: i.label, value: i[nodeKey.value] }))
117
+ treeRef.value?.setCheckedKeys(leftObj.checkedKeys)
118
+ }
119
+ }
120
+
121
+ const rightAllChecked = computed(() => rightObj.checkedKeys.length === rightObj.options.length && rightObj.options.length !== 0)
122
+ function handleRightAllChecked() {
123
+ if (rightAllChecked.value) {
124
+ rightObj.checkedKeys = []
125
+ } else {
126
+ rightObj.checkedKeys = rightObj.options.map((i) => i.value as string)
127
+ }
128
+ }
129
+
130
+ const leftTreeData = computed(() => {
131
+ const treeData = JSON.parse(JSON.stringify(props.data))
132
+ const rightListKeys = rightObj.options.map((i) => i.value)
133
+ const data = filterTree(treeData, (i: any) => !rightListKeys.includes(i[nodeKey.value]))
134
+ return data
135
+ })
136
+
137
+ const handleCheck = (data: any, obj: any) => {
138
+ console.log(data, obj)
139
+ leftObj.checkedKeys = obj.checkedNodes.filter((i: any) => i?.children === undefined).map((j: any) => j[nodeKey.value])
140
+ leftObj.options = obj.checkedNodes.filter((i: any) => i?.children === undefined).map((j: any) => ({ label: j.label, value: j[nodeKey.value] }))
141
+ // leftObj.checkedKeys = obj.checkedNodes.map((i: any) => i[nodeKey.value])
142
+ // leftObj.options = obj.checkedNodes.map((j: any) => ({ label: j.label, value: j[nodeKey.value] }))
143
+ }
144
+
145
+ function getLeftTotal() {
146
+ const data: any[] = []
147
+ eachTree(props.data, (i) => {
148
+ if (i.children === undefined) {
149
+ data.push(i[props?.treeProps?.nodeKey || 'id'])
150
+ }
151
+ })
152
+ return data.length
153
+ }
154
+
155
+ const handleMoveLeft = () => {
156
+ if (rightObj.checkedKeys.length) {
157
+ leftObj.checkedKeys = []
158
+ leftObj.options = []
159
+ rightObj.options = rightObj.options.filter((i) => !rightObj.checkedKeys.includes(i.value as string))
160
+ rightObj.checkedKeys = []
161
+ selectedKeys.value = rightObj.options.map((i) => i.value as string)
162
+ treeRef.value?.setCheckedKeys([])
163
+ }
164
+ }
165
+
166
+ const handleMoveRight = () => {
167
+ rightObj.options = [...rightObj.options, ...leftObj.options]
168
+ leftObj.checkedKeys = []
169
+ leftObj.options = []
170
+ selectedKeys.value = rightObj.options.map((i) => i.value as string)
171
+ }
172
+
173
+ // 获取初始选中
174
+ const setInitChecked = () => {
175
+ const arr: CheckboxOption[] = []
176
+ eachTree(props.data, (i) => {
177
+ if (selectedKeys.value.includes(i[nodeKey.value] as string)) {
178
+ arr.push({ label: i.label, value: i[nodeKey.value] as string })
179
+ }
180
+ })
181
+ rightObj.options = arr
182
+ }
183
+
184
+ onMounted(() => {
185
+ leftObj.total = getLeftTotal()
186
+ setInitChecked()
187
+ })
188
+ </script>
189
+
190
+ <style lang="scss" scoped>
191
+ .el-transfer-panel__list {
192
+ height: auto;
193
+ }
194
+ </style>
@@ -0,0 +1,7 @@
1
+ import type { TreeComponentProps } from 'element-plus'
2
+
3
+ export interface TreeTransferProps {
4
+ titles?: [string, string]
5
+ data: any[]
6
+ treeProps?: Partial<TreeComponentProps>
7
+ }
@@ -0,0 +1,23 @@
1
+ import pkg from 'xe-utils'
2
+
3
+ const { mapTree } = pkg
4
+
5
+ /**
6
+ * @desc 过滤树
7
+ * @param { values } 数组
8
+ */
9
+ type FilterTree = <T extends { children?: T[] }>(
10
+ array: T[],
11
+ iterate: (item: T, index?: number, items?: T[]) => boolean
12
+ ) => T[]
13
+
14
+ export const filterTree: FilterTree = (values, fn) => {
15
+ const arr = values.filter(fn)
16
+ const data = mapTree(arr, (item) => {
17
+ if (item.children && item.children.length) {
18
+ item.children = item.children.filter(fn)
19
+ }
20
+ return item
21
+ })
22
+ return data
23
+ }
@@ -3,7 +3,7 @@
3
3
  // Generated by unplugin-vue-components
4
4
  // Read more: https://github.com/vuejs/core/pull/3399
5
5
  // biome-ignore lint: disable
6
- export {}
6
+ export { }
7
7
 
8
8
  /* prettier-ignore */
9
9
  declare module 'vue' {
@@ -11,6 +11,7 @@ declare module 'vue' {
11
11
  GiButton: typeof import('./components/button/src/button.vue')['default']
12
12
  GiCard: typeof import('./components/card/src/card.vue')['default']
13
13
  GiDialog: typeof import('./components/dialog/src/dialog.vue')['default']
14
+ GiDrawer: typeof import('./components/drawer/src/drawer.vue')['default']
14
15
  GiEditTable: typeof import('./components/edit-table/src/edit-table.vue')['default']
15
16
  GiForm: typeof import('./components/form/src/form.vue')['default']
16
17
  GiGrid: typeof import('./components/grid/src/grid.vue')['default']
@@ -22,5 +23,6 @@ declare module 'vue' {
22
23
  GiTable: typeof import('./components/table/src/table.vue')['default']
23
24
  GiTableColumn: typeof import('./components/table/src/TableColumn.vue')['default']
24
25
  GiTabs: typeof import('./components/tabs/src/tabs.vue')['default']
26
+ GiTreeTransfer: typeof import('./components/tree-transfer/src/tree-transfer.vue')['default']
25
27
  }
26
28
  }
@@ -1,4 +1,4 @@
1
- import type { Ref } from 'vue'
1
+ import type { Ref } from 'vue'
2
2
  import { reactive, ref } from 'vue'
3
3
 
4
4
  interface Options<T, U> {
@@ -94,4 +94,4 @@ export function useTable<T extends U, U = T>(api: UseTableApi<T>, options: Optio
94
94
  /** 刷新 */
95
95
  refresh
96
96
  }
97
- }
97
+ }
package/packages/index.ts CHANGED
@@ -3,6 +3,7 @@ import type { App } from 'vue'
3
3
  import Button from './components/button'
4
4
  import Card from './components/card'
5
5
  import Dialog from './components/dialog'
6
+ import Drawer from './components/drawer'
6
7
  import EditTable from './components/edit-table'
7
8
  import Form from './components/form'
8
9
  import GridItem from './components/grid/src/grid-item.vue'
@@ -12,9 +13,11 @@ import InputSearch from './components/input-search'
12
13
  import PageLayout from './components/page-layout'
13
14
  import Table from './components/table'
14
15
  import Tabs from './components/tabs'
16
+ import TreeTransfer from './components/tree-transfer'
15
17
  import './styles/index.scss'
16
18
 
17
19
  export * from './components/dialog'
20
+ export * from './components/drawer'
18
21
  export * from './components/edit-table'
19
22
  export * from './components/form'
20
23
  export * from './components/table'
@@ -25,6 +28,7 @@ export * from './utils'
25
28
  const components = {
26
29
  Button,
27
30
  Card,
31
+ Drawer,
28
32
  Tabs,
29
33
  InputGroup,
30
34
  InputSearch,
@@ -34,12 +38,14 @@ const components = {
34
38
  PageLayout,
35
39
  Dialog,
36
40
  EditTable,
37
- Table
41
+ Table,
42
+ TreeTransfer
38
43
  }
39
44
 
40
45
  // 导出Gi前缀的组件并添加明确类型注解
41
46
  export const GiButton: typeof Button = Button
42
47
  export const GiCard: typeof Card = Card
48
+ export const GiDrawer: typeof Drawer = Drawer
43
49
  export const GiTabs: typeof Tabs = Tabs
44
50
  export const GiInputGroup: typeof InputGroup = InputGroup
45
51
  export const GiInputSearch: typeof InputSearch = InputSearch
@@ -50,6 +56,7 @@ export const GiPageLayout: typeof PageLayout = PageLayout
50
56
  export const GiDialog: typeof Dialog = Dialog
51
57
  export const GiEditTable: typeof EditTable = EditTable
52
58
  export const GiTable: typeof Table = Table
59
+ export const GiTreeTransfer: typeof TreeTransfer = TreeTransfer
53
60
 
54
61
  function capitalizeWord(word: string) {
55
62
  // 检查输入是否为字符串且不为空
@@ -9,6 +9,29 @@ body {
9
9
  --padding-y-small: 8px;
10
10
  }
11
11
 
12
+ .el-table__body-wrapper tr td.el-table-fixed-column--left,
13
+ .el-table__body-wrapper tr td.el-table-fixed-column--right,
14
+ .el-table__body-wrapper tr th.el-table-fixed-column--left,
15
+ .el-table__body-wrapper tr th.el-table-fixed-column--right,
16
+ .el-table__footer-wrapper tr td.el-table-fixed-column--left,
17
+ .el-table__footer-wrapper tr td.el-table-fixed-column--right,
18
+ .el-table__footer-wrapper tr th.el-table-fixed-column--left,
19
+ .el-table__footer-wrapper tr th.el-table-fixed-column--right,
20
+ .el-table__header-wrapper tr td.el-table-fixed-column--left,
21
+ .el-table__header-wrapper tr td.el-table-fixed-column--right,
22
+ .el-table__header-wrapper tr th.el-table-fixed-column--left,
23
+ .el-table__header-wrapper tr th.el-table-fixed-column--right {
24
+ background: var(--el-bg-color);
25
+ }
26
+
27
+ .gi-table .el-table {
28
+ --el-table-header-bg-color: var(--el-fill-color-lighter);
29
+ }
30
+
31
+ .gi-table .el-table thead.is-group th.el-table__cell {
32
+ background: var(--el-fill-color-lighter);
33
+ }
34
+
12
35
  .gi-card-title {
13
36
  .gi-card-header__title {
14
37
  padding-left: 8px;
@@ -28,12 +51,6 @@ body {
28
51
  }
29
52
  }
30
53
 
31
- .el-dialog__headerbtn {
32
- display: flex;
33
- justify-content: center;
34
- align-items: center;
35
- }
36
-
37
54
  .gi-w-full {
38
55
  width: 100%;
39
56
  }
@@ -180,4 +197,27 @@ body {
180
197
  .el-dialog__footer {
181
198
  border-top: none;
182
199
  }
200
+ }
201
+
202
+ .el-drawer[role=dialog] {
203
+ --el-drawer-padding-primary: 16px;
204
+ }
205
+
206
+ .el-drawer {
207
+ .el-drawer__header {
208
+ height: 48px;
209
+ margin-bottom: 0;
210
+ padding-top: 0;
211
+ border-bottom: 1px solid var(--el-border-color);
212
+ display: flex;
213
+ justify-content: space-between;
214
+ align-items: center;
215
+ box-sizing: border-box;
216
+ }
217
+
218
+ .el-drawer__footer {
219
+ padding-top: 12px;
220
+ padding-bottom: 12px;
221
+ border-top: 1px solid var(--el-border-color);
222
+ }
183
223
  }