gi-component 0.0.9 → 0.0.10

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.10",
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
 
@@ -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' {
@@ -22,5 +22,6 @@ declare module 'vue' {
22
22
  GiTable: typeof import('./components/table/src/table.vue')['default']
23
23
  GiTableColumn: typeof import('./components/table/src/TableColumn.vue')['default']
24
24
  GiTabs: typeof import('./components/tabs/src/tabs.vue')['default']
25
+ GiTreeTransfer: typeof import('./components/tree-transfer/src/tree-transfer.vue')['default']
25
26
  }
26
27
  }
@@ -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
@@ -12,6 +12,7 @@ import InputSearch from './components/input-search'
12
12
  import PageLayout from './components/page-layout'
13
13
  import Table from './components/table'
14
14
  import Tabs from './components/tabs'
15
+ import TreeTransfer from './components/tree-transfer'
15
16
  import './styles/index.scss'
16
17
 
17
18
  export * from './components/dialog'
@@ -34,7 +35,8 @@ const components = {
34
35
  PageLayout,
35
36
  Dialog,
36
37
  EditTable,
37
- Table
38
+ Table,
39
+ TreeTransfer
38
40
  }
39
41
 
40
42
  // 导出Gi前缀的组件并添加明确类型注解
@@ -50,6 +52,7 @@ export const GiPageLayout: typeof PageLayout = PageLayout
50
52
  export const GiDialog: typeof Dialog = Dialog
51
53
  export const GiEditTable: typeof EditTable = EditTable
52
54
  export const GiTable: typeof Table = Table
55
+ export const GiTreeTransfer: typeof TreeTransfer = TreeTransfer
53
56
 
54
57
  function capitalizeWord(word: string) {
55
58
  // 检查输入是否为字符串且不为空
@@ -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;