@peng_kai/kit 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/admin/components/scroll-nav/src/ScrollNav.vue +59 -59
- package/admin/components/text/index.ts +13 -13
- package/admin/components/text/src/Amount.vue +114 -114
- package/admin/components/text/src/Datetime.vue +44 -45
- package/admin/components/text/src/Duration.vue +26 -26
- package/admin/components/text/src/Hash.vue +40 -40
- package/admin/components/text/src/createTagGetter.ts +13 -13
- package/admin/filter/FilterDrawer.vue +96 -96
- package/admin/filter/FilterParam.vue +76 -76
- package/admin/hooks/useMenu.ts +128 -132
- package/admin/hooks/usePage.ts +139 -138
- package/admin/hooks/usePageTab.ts +35 -35
- package/admin/layout/large/Breadcrumb.vue +70 -68
- package/admin/layout/large/Content.vue +24 -23
- package/admin/layout/large/Menu.vue +68 -73
- package/admin/layout/large/PageTab.vue +71 -70
- package/antd/components/InputNumberRange.vue +47 -47
- package/antd/hooks/useAntdDrawer.ts +73 -73
- package/antd/hooks/useAntdTable.ts +70 -70
- package/components/infinite-query/index.ts +1 -1
- package/components/infinite-query/src/InfiniteQuery.vue +147 -147
- package/components/infinite-query/src/useCreateTrigger.ts +35 -35
- package/kitDependencies.ts +26 -6
- package/package.json +40 -40
- package/request/helpers.ts +32 -32
- package/request/type.d.ts +89 -89
- package/tsconfig.json +17 -17
- package/pnpm-lock.yaml +0 -598
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { ref } from "vue";
|
|
3
|
-
import { Drawer as ADrawer, Button as AButton } from "ant-design-vue";
|
|
4
|
-
|
|
5
|
-
const emits = defineEmits<{
|
|
6
|
-
(e: 'filter'): void
|
|
7
|
-
(e: 'reset', value: number): void
|
|
8
|
-
}>()
|
|
9
|
-
const filterVisible = ref(false)
|
|
10
|
-
|
|
11
|
-
function filter() {
|
|
12
|
-
emits('filter')
|
|
13
|
-
filterVisible.value = false
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function reset() {
|
|
17
|
-
emits('reset', 1)
|
|
18
|
-
filterVisible.value = false
|
|
19
|
-
}
|
|
20
|
-
</script>
|
|
21
|
-
|
|
22
|
-
<template>
|
|
23
|
-
<div class="p-3 mb-2 bg-white text-14px" @click="filterVisible = true">
|
|
24
|
-
<!-- .filter-params 为空时显示 .filter-params-tips -->
|
|
25
|
-
<div class="filter-params">
|
|
26
|
-
<slot name="params" />
|
|
27
|
-
</div>
|
|
28
|
-
<div class="filter-params-tips">
|
|
29
|
-
条件筛选,点击打开
|
|
30
|
-
</div>
|
|
31
|
-
</div>
|
|
32
|
-
|
|
33
|
-
<ADrawer v-model:open="filterVisible" class="filter-drawer" placement="bottom" height="50vh">
|
|
34
|
-
<template #extra>
|
|
35
|
-
<AButton class="mr-3 my--3" @click="reset()">
|
|
36
|
-
重置
|
|
37
|
-
</AButton>
|
|
38
|
-
<AButton class="my--3" type="primary" @click="filter()">
|
|
39
|
-
筛选
|
|
40
|
-
</AButton>
|
|
41
|
-
</template>
|
|
42
|
-
<template #default>
|
|
43
|
-
<slot />
|
|
44
|
-
</template>
|
|
45
|
-
</ADrawer>
|
|
46
|
-
</template>
|
|
47
|
-
|
|
48
|
-
<style scoped lang="scss">
|
|
49
|
-
.filter-params {
|
|
50
|
-
display: flex;
|
|
51
|
-
flex-wrap: wrap;
|
|
52
|
-
justify-content: flex-start;
|
|
53
|
-
gap: 5px 15px;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// .filter-params 为空时显示 .filter-params-tips
|
|
57
|
-
.filter-params-tips {
|
|
58
|
-
display: none;
|
|
59
|
-
color: theme('colors.gray.DEFAULT');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.filter-params:empty {
|
|
63
|
-
display: none;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.filter-params:empty + .filter-params-tips {
|
|
67
|
-
display: block;
|
|
68
|
-
}
|
|
69
|
-
</style>
|
|
70
|
-
|
|
71
|
-
<style lang="scss">
|
|
72
|
-
.filter-drawer {
|
|
73
|
-
.ant-drawer-header {
|
|
74
|
-
padding: 16px;
|
|
75
|
-
|
|
76
|
-
.ant-drawer-close {
|
|
77
|
-
--expand: 5px;
|
|
78
|
-
|
|
79
|
-
padding: var(--expand);
|
|
80
|
-
margin: calc(var(--expand) * -1) var(--expand) calc(var(--expand) * -1) 0;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.ant-drawer-body {
|
|
85
|
-
padding: 16px;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.ant-form-item {
|
|
89
|
-
margin-bottom: 0;
|
|
90
|
-
|
|
91
|
-
.ant-form-item-label {
|
|
92
|
-
padding-bottom: 0;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
</style>
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref } from "vue";
|
|
3
|
+
import { Drawer as ADrawer, Button as AButton } from "ant-design-vue";
|
|
4
|
+
|
|
5
|
+
const emits = defineEmits<{
|
|
6
|
+
(e: 'filter'): void
|
|
7
|
+
(e: 'reset', value: number): void
|
|
8
|
+
}>()
|
|
9
|
+
const filterVisible = ref(false)
|
|
10
|
+
|
|
11
|
+
function filter() {
|
|
12
|
+
emits('filter')
|
|
13
|
+
filterVisible.value = false
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function reset() {
|
|
17
|
+
emits('reset', 1)
|
|
18
|
+
filterVisible.value = false
|
|
19
|
+
}
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div class="p-3 mb-2 bg-white text-14px" @click="filterVisible = true">
|
|
24
|
+
<!-- .filter-params 为空时显示 .filter-params-tips -->
|
|
25
|
+
<div class="filter-params">
|
|
26
|
+
<slot name="params" />
|
|
27
|
+
</div>
|
|
28
|
+
<div class="filter-params-tips">
|
|
29
|
+
条件筛选,点击打开
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<ADrawer v-model:open="filterVisible" class="filter-drawer" placement="bottom" height="50vh">
|
|
34
|
+
<template #extra>
|
|
35
|
+
<AButton class="mr-3 my--3" @click="reset()">
|
|
36
|
+
重置
|
|
37
|
+
</AButton>
|
|
38
|
+
<AButton class="my--3" type="primary" @click="filter()">
|
|
39
|
+
筛选
|
|
40
|
+
</AButton>
|
|
41
|
+
</template>
|
|
42
|
+
<template #default>
|
|
43
|
+
<slot />
|
|
44
|
+
</template>
|
|
45
|
+
</ADrawer>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<style scoped lang="scss">
|
|
49
|
+
.filter-params {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-wrap: wrap;
|
|
52
|
+
justify-content: flex-start;
|
|
53
|
+
gap: 5px 15px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// .filter-params 为空时显示 .filter-params-tips
|
|
57
|
+
.filter-params-tips {
|
|
58
|
+
display: none;
|
|
59
|
+
color: theme('colors.gray.DEFAULT');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.filter-params:empty {
|
|
63
|
+
display: none;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.filter-params:empty + .filter-params-tips {
|
|
67
|
+
display: block;
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
70
|
+
|
|
71
|
+
<style lang="scss">
|
|
72
|
+
.filter-drawer {
|
|
73
|
+
.ant-drawer-header {
|
|
74
|
+
padding: 16px;
|
|
75
|
+
|
|
76
|
+
.ant-drawer-close {
|
|
77
|
+
--expand: 5px;
|
|
78
|
+
|
|
79
|
+
padding: var(--expand);
|
|
80
|
+
margin: calc(var(--expand) * -1) var(--expand) calc(var(--expand) * -1) 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.ant-drawer-body {
|
|
85
|
+
padding: 16px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.ant-form-item {
|
|
89
|
+
margin-bottom: 0;
|
|
90
|
+
|
|
91
|
+
.ant-form-item-label {
|
|
92
|
+
padding-bottom: 0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
</style>
|
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import isNil from 'lodash-es/isNil'
|
|
3
|
-
import isFinite from 'lodash-es/isFinite'
|
|
4
|
-
import dayjs from 'dayjs'
|
|
5
|
-
import bignumber from 'bignumber.js'
|
|
6
|
-
|
|
7
|
-
export const paramTypes = { numberRange, datetimeRange, options }
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 时间范围格式化
|
|
11
|
-
* @param range 数字范围
|
|
12
|
-
* @param unit 单位
|
|
13
|
-
*/
|
|
14
|
-
function numberRange(range?: [number, number], unit?: string) {
|
|
15
|
-
if (!range?.every(isFinite))
|
|
16
|
-
return ''
|
|
17
|
-
|
|
18
|
-
return `${bignumber(range[0]).toFormat()}~${bignumber(range[1]).toFormat()}${unit}`
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 时间范围格式化
|
|
23
|
-
* @param range 时间范围
|
|
24
|
-
* @param template 格式化模板(文档:https://dayjs.gitee.io/docs/zh-CN/display/format )
|
|
25
|
-
*/
|
|
26
|
-
function datetimeRange(range?: [string | dayjs.Dayjs, string | dayjs.Dayjs], template = 'YYYY-MM-DD') {
|
|
27
|
-
if (!range?.every(v => dayjs(v).isValid()))
|
|
28
|
-
return ''
|
|
29
|
-
|
|
30
|
-
return `${dayjs(range[0]).format(template)} ~ ${dayjs(range[1]).format(template)}`
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function options(
|
|
34
|
-
value?: string | number | Array<string | number>,
|
|
35
|
-
options?: Array<{ value: string | number; label: any }>,
|
|
36
|
-
) {
|
|
37
|
-
if (isNil(value) || isNil(options))
|
|
38
|
-
return
|
|
39
|
-
if (value === '')
|
|
40
|
-
return
|
|
41
|
-
|
|
42
|
-
const _value = Array.isArray(value) ? value : [value]
|
|
43
|
-
|
|
44
|
-
return options
|
|
45
|
-
.filter(o => _value.includes(o.value))
|
|
46
|
-
.map(o => o.label)
|
|
47
|
-
.join(', ')
|
|
48
|
-
}
|
|
49
|
-
</script>
|
|
50
|
-
|
|
51
|
-
<script setup lang="ts">
|
|
52
|
-
const props = defineProps<{
|
|
53
|
-
label: string
|
|
54
|
-
content?: any
|
|
55
|
-
}>()
|
|
56
|
-
</script>
|
|
57
|
-
|
|
58
|
-
<template>
|
|
59
|
-
<div v-if="props.content" class="item-param">
|
|
60
|
-
<span class="label">{{ props.label }}</span>
|
|
61
|
-
<span class="content">{{ props.content }}</span>
|
|
62
|
-
</div>
|
|
63
|
-
</template>
|
|
64
|
-
|
|
65
|
-
<style lang="scss" scoped>
|
|
66
|
-
.label {
|
|
67
|
-
display: inline-block;
|
|
68
|
-
margin-right: 0.3em;
|
|
69
|
-
color: theme('colors.gray.DEFAULT');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.content {
|
|
73
|
-
color: theme('colors.primary.DEFAULT');
|
|
74
|
-
word-break: break-all;
|
|
75
|
-
}
|
|
76
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import isNil from 'lodash-es/isNil'
|
|
3
|
+
import isFinite from 'lodash-es/isFinite'
|
|
4
|
+
import dayjs from 'dayjs'
|
|
5
|
+
import bignumber from 'bignumber.js'
|
|
6
|
+
|
|
7
|
+
export const paramTypes = { numberRange, datetimeRange, options }
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 时间范围格式化
|
|
11
|
+
* @param range 数字范围
|
|
12
|
+
* @param unit 单位
|
|
13
|
+
*/
|
|
14
|
+
function numberRange(range?: [number, number], unit?: string) {
|
|
15
|
+
if (!range?.every(isFinite))
|
|
16
|
+
return ''
|
|
17
|
+
|
|
18
|
+
return `${bignumber(range[0]).toFormat()}~${bignumber(range[1]).toFormat()}${unit}`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 时间范围格式化
|
|
23
|
+
* @param range 时间范围
|
|
24
|
+
* @param template 格式化模板(文档:https://dayjs.gitee.io/docs/zh-CN/display/format )
|
|
25
|
+
*/
|
|
26
|
+
function datetimeRange(range?: [string | dayjs.Dayjs, string | dayjs.Dayjs], template = 'YYYY-MM-DD') {
|
|
27
|
+
if (!range?.every(v => dayjs(v).isValid()))
|
|
28
|
+
return ''
|
|
29
|
+
|
|
30
|
+
return `${dayjs(range[0]).format(template)} ~ ${dayjs(range[1]).format(template)}`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function options(
|
|
34
|
+
value?: string | number | Array<string | number>,
|
|
35
|
+
options?: Array<{ value: string | number; label: any }>,
|
|
36
|
+
) {
|
|
37
|
+
if (isNil(value) || isNil(options))
|
|
38
|
+
return
|
|
39
|
+
if (value === '')
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
const _value = Array.isArray(value) ? value : [value]
|
|
43
|
+
|
|
44
|
+
return options
|
|
45
|
+
.filter(o => _value.includes(o.value))
|
|
46
|
+
.map(o => o.label)
|
|
47
|
+
.join(', ')
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<script setup lang="ts">
|
|
52
|
+
const props = defineProps<{
|
|
53
|
+
label: string
|
|
54
|
+
content?: any
|
|
55
|
+
}>()
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<div v-if="props.content" class="item-param">
|
|
60
|
+
<span class="label">{{ props.label }}</span>
|
|
61
|
+
<span class="content">{{ props.content }}</span>
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
|
|
65
|
+
<style lang="scss" scoped>
|
|
66
|
+
.label {
|
|
67
|
+
display: inline-block;
|
|
68
|
+
margin-right: 0.3em;
|
|
69
|
+
color: theme('colors.gray.DEFAULT');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.content {
|
|
73
|
+
color: theme('colors.primary.DEFAULT');
|
|
74
|
+
word-break: break-all;
|
|
75
|
+
}
|
|
76
|
+
</style>
|
package/admin/hooks/useMenu.ts
CHANGED
|
@@ -1,132 +1,128 @@
|
|
|
1
|
-
import { createGlobalState } from '@vueuse/core'
|
|
2
|
-
import { reactive, ref, computed, watch } from 'vue'
|
|
3
|
-
import type { UnwrapNestedRefs, VNode, Ref } from 'vue'
|
|
4
|
-
|
|
5
|
-
export { useMenu }
|
|
6
|
-
export type { TMenu }
|
|
7
|
-
|
|
8
|
-
interface IMenuConfig {
|
|
9
|
-
key: string
|
|
10
|
-
label: string | (() => string)
|
|
11
|
-
icon?: () => VNode
|
|
12
|
-
order: number
|
|
13
|
-
trigger: () => void
|
|
14
|
-
children?: IMenuConfig[]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface IMenuReactive {
|
|
18
|
-
key: string
|
|
19
|
-
label: Ref<string>
|
|
20
|
-
icon: Ref<VNode | null>
|
|
21
|
-
order: number
|
|
22
|
-
trigger: () => void
|
|
23
|
-
children?: IMenuReactive[]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
type TMenu = UnwrapNestedRefs<IMenuReactive>
|
|
27
|
-
|
|
28
|
-
const useMenu = createGlobalState(() => {
|
|
29
|
-
const menus = reactive<TMenu[]>([])
|
|
30
|
-
const collapsed = ref(false)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (menu.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return { menus, collapsed, getMenuPath, addMenu, removeMenu, getMenu }
|
|
132
|
-
})
|
|
1
|
+
import { createGlobalState } from '@vueuse/core'
|
|
2
|
+
import { reactive, ref, computed, watch } from 'vue'
|
|
3
|
+
import type { UnwrapNestedRefs, VNode, Ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
export { useMenu }
|
|
6
|
+
export type { TMenu }
|
|
7
|
+
|
|
8
|
+
interface IMenuConfig {
|
|
9
|
+
key: string
|
|
10
|
+
label: string | (() => string)
|
|
11
|
+
icon?: () => VNode
|
|
12
|
+
order: number
|
|
13
|
+
trigger: () => void
|
|
14
|
+
children?: IMenuConfig[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface IMenuReactive {
|
|
18
|
+
key: string
|
|
19
|
+
label: Ref<string>
|
|
20
|
+
icon: Ref<VNode | null>
|
|
21
|
+
order: number
|
|
22
|
+
trigger: () => void
|
|
23
|
+
children?: IMenuReactive[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type TMenu = UnwrapNestedRefs<IMenuReactive>
|
|
27
|
+
|
|
28
|
+
const useMenu = createGlobalState(() => {
|
|
29
|
+
const menus = reactive<TMenu[]>([])
|
|
30
|
+
const collapsed = ref(false)
|
|
31
|
+
|
|
32
|
+
const findMenu = (menus: TMenu[], key: string) => {
|
|
33
|
+
const queue = [...menus]
|
|
34
|
+
|
|
35
|
+
while (queue.length > 0) {
|
|
36
|
+
const menu = queue.shift()!
|
|
37
|
+
|
|
38
|
+
if (menu.key === key)
|
|
39
|
+
return menu
|
|
40
|
+
|
|
41
|
+
if (menu.children)
|
|
42
|
+
queue.push(...menu.children)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 获取目标路由的路径
|
|
50
|
+
* @param key 目标路由
|
|
51
|
+
*/
|
|
52
|
+
const getMenuPath = (key: string) => {
|
|
53
|
+
const path: TMenu[] = []
|
|
54
|
+
|
|
55
|
+
const _getMenuPath = (menus: TMenu[], key: string) => {
|
|
56
|
+
for (const menu of menus) {
|
|
57
|
+
if (menu.key === key) {
|
|
58
|
+
path.push(menu)
|
|
59
|
+
return true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (menu.children) {
|
|
63
|
+
path.push(menu)
|
|
64
|
+
if (_getMenuPath(menu.children, key))
|
|
65
|
+
return true
|
|
66
|
+
path.pop()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_getMenuPath(menus, key)
|
|
74
|
+
|
|
75
|
+
return path
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const addMenu = (menuConfig: IMenuConfig, parentKey?: string) => {
|
|
79
|
+
const labelGetter = typeof menuConfig.label === 'function' ? menuConfig.label : (() => menuConfig.label) as (() => string)
|
|
80
|
+
const iconGetter = typeof menuConfig.icon === 'function' ? menuConfig.icon : () => null
|
|
81
|
+
|
|
82
|
+
const _menu = reactive<IMenuReactive>({
|
|
83
|
+
key: menuConfig.key,
|
|
84
|
+
label: computed(labelGetter),
|
|
85
|
+
icon: computed(iconGetter),
|
|
86
|
+
trigger: menuConfig.trigger,
|
|
87
|
+
order: menuConfig.order,
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
if (parentKey) {
|
|
91
|
+
const parentMenu = findMenu(menus, parentKey)
|
|
92
|
+
|
|
93
|
+
if (!parentMenu)
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
const children = reactive(parentMenu.children ?? [])
|
|
97
|
+
children.push(_menu)
|
|
98
|
+
children.sort((a, b) => a.order - b.order)
|
|
99
|
+
parentMenu.children = children
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
menus.push(_menu)
|
|
103
|
+
menus.sort((a, b) => a.order - b.order)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const removeMenu = (key: string) => {
|
|
108
|
+
const _remove = (menus: TMenu[], key: string) => {
|
|
109
|
+
for (let i = 0; i < menus.length; i++) {
|
|
110
|
+
const menu = menus[i]
|
|
111
|
+
if (menu.key === key) {
|
|
112
|
+
menus.splice(i, 1)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
if (menu.children)
|
|
116
|
+
_remove(menu.children, key)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return _remove(menus, key)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const getMenu = (key: string) => {
|
|
124
|
+
return findMenu(menus, key)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { menus, collapsed, getMenuPath, addMenu, removeMenu, getMenu }
|
|
128
|
+
})
|