@peng_kai/kit 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/components/filter/src/FilterDrawer.vue +59 -9
- package/admin/components/filter/src/FilterReset.vue +34 -7
- package/admin/components/record/index.ts +1 -0
- package/admin/components/record/src/Record.vue +83 -0
- package/admin/components/text/src/Amount.vue +1 -1
- package/admin/components/text/src/Datetime.vue +6 -2
- package/admin/components/text/src/Hash.vue +2 -2
- package/admin/layout/large/Menu.vue +1 -1
- package/admin/route-guards/collapseMenu.ts +11 -0
- package/admin/route-guards/index.ts +1 -0
- package/admin/route-guards/pageProgress.ts +21 -10
- package/admin/route-guards/pageTitle.ts +13 -18
- package/admin/styles/index.scss +2 -2
- package/admin/unocss/index.ts +2 -2
- package/antd/hooks/useAntdTable.ts +1 -0
- package/package.json +1 -1
- package/utils/index.ts +5 -0
- package/vue/hooks/useIsDark.ts +21 -0
- package/vue/index.ts +1 -0
|
@@ -1,36 +1,77 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { ref } from 'vue';
|
|
3
|
-
import { Button as AButton, Drawer as ADrawer } from 'ant-design-vue';
|
|
2
|
+
import { computed, ref } from 'vue';
|
|
3
|
+
import { Button as AButton, Drawer as ADrawer, Dropdown as ADropdown } from 'ant-design-vue';
|
|
4
4
|
|
|
5
5
|
defineOptions({
|
|
6
6
|
inheritAttrs: false,
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
+
const props = withDefaults(defineProps<{
|
|
10
|
+
loading?: boolean
|
|
11
|
+
filterQuery?: any
|
|
12
|
+
filterParams?: any
|
|
13
|
+
filterForm?: any
|
|
14
|
+
}>(), {
|
|
15
|
+
loading: undefined,
|
|
16
|
+
});
|
|
9
17
|
const emits = defineEmits<{
|
|
10
18
|
(e: 'filter'): void
|
|
11
19
|
(e: 'reset', value: number): void
|
|
12
20
|
}>();
|
|
21
|
+
|
|
13
22
|
const filterVisible = ref(false);
|
|
23
|
+
const loading = computed(() => {
|
|
24
|
+
const _loading1 = props.loading;
|
|
25
|
+
const _loading2: boolean = props.filterQuery?.isFetching.value;
|
|
26
|
+
|
|
27
|
+
if (_loading1 === undefined && _loading2 === undefined)
|
|
28
|
+
return false;
|
|
29
|
+
|
|
30
|
+
if (_loading1 !== undefined)
|
|
31
|
+
return _loading1;
|
|
32
|
+
|
|
33
|
+
return _loading2;
|
|
34
|
+
});
|
|
14
35
|
|
|
15
36
|
function filter() {
|
|
37
|
+
props.filterParams?.update?.();
|
|
16
38
|
emits('filter');
|
|
17
39
|
filterVisible.value = false;
|
|
18
40
|
}
|
|
19
41
|
|
|
20
42
|
function reset() {
|
|
43
|
+
props.filterForm?.$form.resetFields?.();
|
|
44
|
+
props.filterParams?.update?.();
|
|
21
45
|
emits('reset', 1);
|
|
22
46
|
filterVisible.value = false;
|
|
23
47
|
}
|
|
24
48
|
</script>
|
|
25
49
|
|
|
26
50
|
<template>
|
|
27
|
-
<div class="p-3
|
|
51
|
+
<div class="p-3 bg-$antd-colorBgContainer text-14px rounded-2" v-bind="$attrs">
|
|
28
52
|
<!-- .filter-params 为空时显示 .filter-params-tips -->
|
|
29
|
-
|
|
30
|
-
|
|
53
|
+
|
|
54
|
+
<div class="m--3 p-3" @click="filterVisible = true">
|
|
55
|
+
<div class="filter-params">
|
|
56
|
+
<slot name="params" />
|
|
57
|
+
</div>
|
|
58
|
+
<div class="filter-params-tips">
|
|
59
|
+
条件筛选,点击打开
|
|
60
|
+
</div>
|
|
31
61
|
</div>
|
|
32
|
-
|
|
33
|
-
|
|
62
|
+
|
|
63
|
+
<!-- 操作区 -->
|
|
64
|
+
<div v-if="$slots.actions || $slots.moreActions" class="actions">
|
|
65
|
+
<slot name="actions" />
|
|
66
|
+
|
|
67
|
+
<ADropdown v-if="$slots.moreActions" trigger="click" destroyPopupOnHide>
|
|
68
|
+
<AButton type="link" size="small">
|
|
69
|
+
更多
|
|
70
|
+
</AButton>
|
|
71
|
+
<template #overlay>
|
|
72
|
+
<slot name="moreActions" />
|
|
73
|
+
</template>
|
|
74
|
+
</ADropdown>
|
|
34
75
|
</div>
|
|
35
76
|
</div>
|
|
36
77
|
|
|
@@ -39,10 +80,10 @@ function reset() {
|
|
|
39
80
|
height="50vh"
|
|
40
81
|
>
|
|
41
82
|
<template #extra>
|
|
42
|
-
<AButton class="mr-3 my--3" @click="reset()">
|
|
83
|
+
<AButton class="mr-3 my--3" :disabled="loading" @click="reset()">
|
|
43
84
|
重置
|
|
44
85
|
</AButton>
|
|
45
|
-
<AButton class="my--3" type="primary" @click="filter()">
|
|
86
|
+
<AButton class="my--3" type="primary" :loading="loading" @click="filter()">
|
|
46
87
|
筛选
|
|
47
88
|
</AButton>
|
|
48
89
|
</template>
|
|
@@ -73,6 +114,15 @@ function reset() {
|
|
|
73
114
|
.filter-params:empty + .filter-params-tips {
|
|
74
115
|
display: block;
|
|
75
116
|
}
|
|
117
|
+
|
|
118
|
+
.actions {
|
|
119
|
+
display: flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
justify-content: flex-end;
|
|
122
|
+
border-top: 1px solid var(--antd-colorBorderSecondary);
|
|
123
|
+
margin-top: 0.75rem;
|
|
124
|
+
padding-top: 0.75rem;
|
|
125
|
+
}
|
|
76
126
|
</style>
|
|
77
127
|
|
|
78
128
|
<style lang="scss">
|
|
@@ -1,24 +1,51 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Button as AButton } from 'ant-design-vue';
|
|
3
|
+
import { computed } from 'vue';
|
|
3
4
|
|
|
4
|
-
const props = defineProps<{
|
|
5
|
+
const props = withDefaults(defineProps<{
|
|
5
6
|
loading?: boolean
|
|
6
|
-
|
|
7
|
+
filterQuery?: any
|
|
8
|
+
filterParams?: any
|
|
9
|
+
filterForm?: any
|
|
10
|
+
}>(), {
|
|
11
|
+
loading: undefined,
|
|
12
|
+
});
|
|
7
13
|
const emits = defineEmits<{
|
|
8
14
|
(e: 'filter'): void
|
|
9
15
|
(e: 'reset'): void
|
|
10
16
|
}>();
|
|
17
|
+
|
|
18
|
+
const loading = computed(() => {
|
|
19
|
+
const _loading1 = props.loading;
|
|
20
|
+
const _loading2: boolean = props.filterQuery?.isFetching.value;
|
|
21
|
+
|
|
22
|
+
if (_loading1 === undefined && _loading2 === undefined)
|
|
23
|
+
return false;
|
|
24
|
+
|
|
25
|
+
if (_loading1 !== undefined)
|
|
26
|
+
return _loading1;
|
|
27
|
+
|
|
28
|
+
return _loading2;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
function filter() {
|
|
32
|
+
props.filterParams?.update?.();
|
|
33
|
+
emits('filter');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function reset() {
|
|
37
|
+
props.filterForm?.$form.resetFields?.();
|
|
38
|
+
props.filterParams?.update?.({ page: 1 });
|
|
39
|
+
emits('reset');
|
|
40
|
+
}
|
|
11
41
|
</script>
|
|
12
42
|
|
|
13
43
|
<template>
|
|
14
44
|
<div class="flex-none flex w-min ml-auto">
|
|
15
|
-
<AButton
|
|
16
|
-
class="mr-2 filter-btn" type="primary" htmlType="submit"
|
|
17
|
-
:loading="props.loading" @click="emits('filter')"
|
|
18
|
-
>
|
|
45
|
+
<AButton class="mr-2 filter-btn" type="primary" htmlType="submit" :loading="loading" @click="filter()">
|
|
19
46
|
查询
|
|
20
47
|
</AButton>
|
|
21
|
-
<AButton :disabled="
|
|
48
|
+
<AButton :disabled="loading" @click="reset()">
|
|
22
49
|
重置
|
|
23
50
|
</AButton>
|
|
24
51
|
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Record } from './src/Record.vue';
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
import { Card as ACard, Dropdown as ADropdown } from 'ant-design-vue';
|
|
4
|
+
|
|
5
|
+
const collapsed = ref(false);
|
|
6
|
+
const $actions = ref<HTMLDivElement>();
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<ACard :bordered="false" size="small" class="my-3 relative">
|
|
11
|
+
<template #title>
|
|
12
|
+
<div class="flex items-center min-h-10">
|
|
13
|
+
<div class="title">
|
|
14
|
+
<slot name="title" />
|
|
15
|
+
</div>
|
|
16
|
+
<div class="flex gap-2 ml-auto">
|
|
17
|
+
<ADropdown v-if="$slots.moreActions" trigger="click" destroyPopupOnHide>
|
|
18
|
+
<div ref="$actions" class="action-btn" tabindex="-1">
|
|
19
|
+
<i class="i-ri:more-fill" />
|
|
20
|
+
</div>
|
|
21
|
+
<template #overlay>
|
|
22
|
+
<slot name="moreActions" />
|
|
23
|
+
</template>
|
|
24
|
+
</ADropdown>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<template #default>
|
|
30
|
+
<slot name="default" />
|
|
31
|
+
|
|
32
|
+
<div v-auto-animate>
|
|
33
|
+
<slot v-if="collapsed" name="collapsible" />
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div v-if="$slots.collapsible" class="more-btn" :class="{ collapsed }" tabindex="-1" @click="collapsed = !collapsed">
|
|
37
|
+
<i class="i-mingcute:down-line arrow" />
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
40
|
+
</ACard>
|
|
41
|
+
</template>
|
|
42
|
+
|
|
43
|
+
<style scoped lang="scss">
|
|
44
|
+
.action-btn {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
width: 26px;
|
|
49
|
+
height: 26px;
|
|
50
|
+
margin: 0;
|
|
51
|
+
font-size: 1.4em;
|
|
52
|
+
line-height: 1em;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.more-btn {
|
|
56
|
+
position: absolute;
|
|
57
|
+
right: 12px;
|
|
58
|
+
bottom: 12px;
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
width: 26px;
|
|
63
|
+
height: 26px;
|
|
64
|
+
line-height: 1em;
|
|
65
|
+
background-color: rgb(133 133 133 / 30%);
|
|
66
|
+
border-radius: 5px;
|
|
67
|
+
opacity: 0.5;
|
|
68
|
+
|
|
69
|
+
&:active {
|
|
70
|
+
transform: scale(0.9);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.arrow {
|
|
74
|
+
display: block;
|
|
75
|
+
font-size: 1.4em;
|
|
76
|
+
transition: 200ms;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&.collapsed .arrow {
|
|
80
|
+
transform: rotate(180deg);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
@@ -82,7 +82,7 @@ const symbol = computed(() => presetSymbols[props.symbol!] ?? props.symbol ?? ''
|
|
|
82
82
|
<span v-else class="color-$amount-color">{{ symbol }}</span> <!-- 文本Logo,如法定币种符号(¥、$) -->
|
|
83
83
|
|
|
84
84
|
<!-- 金额 -->
|
|
85
|
-
<span class="color-$amount-color
|
|
85
|
+
<span class="color-$amount-color amount">{{ amountText }}</span>
|
|
86
86
|
|
|
87
87
|
<!-- 单位 -->
|
|
88
88
|
<span v-if="props.unit" class="unit">{{ props.unit }}</span>
|
|
@@ -37,8 +37,12 @@ const timestamp = computed(() => {
|
|
|
37
37
|
<div>{{ dayjs(timestamp).fromNow?.() }}</div>
|
|
38
38
|
<div>{{ dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss') }}</div>
|
|
39
39
|
</template>
|
|
40
|
-
<span v-bind="$attrs">{{ timestamp ? dayjs(timestamp).format(props.template) : '-' }}</span>
|
|
40
|
+
<span v-bind="$attrs" class="text">{{ timestamp ? dayjs(timestamp).format(props.template) : '-' }}</span>
|
|
41
41
|
</ATooltip>
|
|
42
42
|
</template>
|
|
43
43
|
|
|
44
|
-
<style scoped lang="scss"
|
|
44
|
+
<style scoped lang="scss">
|
|
45
|
+
.text {
|
|
46
|
+
font-variant-numeric: tabular-nums;
|
|
47
|
+
}
|
|
48
|
+
</style>
|
|
@@ -43,9 +43,9 @@ const text = computed(() => {
|
|
|
43
43
|
<ATypographyLink :href="href" :copyable="{ text: props.hash, tooltip: false }" target="_blank">
|
|
44
44
|
<ATooltip>
|
|
45
45
|
<template v-if="props.hide" #title>
|
|
46
|
-
{{ props.hash }}
|
|
46
|
+
<span class="font-mono">{{ props.hash }}</span>
|
|
47
47
|
</template>
|
|
48
|
-
<span v-bind="$attrs"><slot>{{ text }}</slot></span>
|
|
48
|
+
<span v-bind="$attrs"><slot><span class="font-mono">{{ text }}</span></slot></span>
|
|
49
49
|
</ATooltip>
|
|
50
50
|
</ATypographyLink>
|
|
51
51
|
</template>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Router } from 'vue-router';
|
|
2
|
+
import { getDependencies } from '../../kitDependencies';
|
|
3
|
+
|
|
4
|
+
export function setupCollapseMenu(router: Router) {
|
|
5
|
+
const deps = getDependencies();
|
|
6
|
+
|
|
7
|
+
router.beforeEach(() => {
|
|
8
|
+
deps.useMenu().collapsed.value = false;
|
|
9
|
+
return true;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
import type { Router } from 'vue-router';
|
|
2
|
-
import {
|
|
2
|
+
import { useStyleTag } from '@vueuse/core';
|
|
3
|
+
import NProgress from 'nprogress';
|
|
4
|
+
import 'nprogress/nprogress.css';
|
|
3
5
|
|
|
6
|
+
/**
|
|
7
|
+
* 用于显示页面跳转时,页面顶部进度条
|
|
8
|
+
*/
|
|
4
9
|
export function setupPageProgress(router: Router) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
: title ?? deps.appName;
|
|
10
|
+
NProgress.configure({ showSpinner: false });
|
|
11
|
+
useStyleTag(`
|
|
12
|
+
#nprogress .bar {
|
|
13
|
+
height: 3px;
|
|
14
|
+
background: var(--antd-colorPrimary);
|
|
15
|
+
}
|
|
16
|
+
#nprogress .peg {
|
|
17
|
+
box-shadow: 0 0 10px var(--antd-colorPrimary), 0 0 5px var(--antd-colorPrimary);
|
|
14
18
|
}
|
|
19
|
+
`);
|
|
20
|
+
|
|
21
|
+
router.beforeEach(() => {
|
|
22
|
+
NProgress.start();
|
|
23
|
+
});
|
|
24
|
+
router.afterEach(() => {
|
|
25
|
+
setTimeout(() => NProgress.done(), 200);
|
|
15
26
|
});
|
|
16
27
|
}
|
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
import type { Router } from 'vue-router';
|
|
2
|
-
import {
|
|
3
|
-
import NProgress from 'nprogress';
|
|
4
|
-
import 'nprogress/nprogress.css';
|
|
2
|
+
import { getDependencies } from '../../kitDependencies';
|
|
5
3
|
|
|
4
|
+
/**
|
|
5
|
+
* 用于在路由跳转后,同步修改 title
|
|
6
|
+
*/
|
|
6
7
|
export function setupPageTitle(router: Router) {
|
|
7
|
-
|
|
8
|
-
useStyleTag(`
|
|
9
|
-
#nprogress .bar {
|
|
10
|
-
height: 3px;
|
|
11
|
-
background: var(--antd-colorPrimary);
|
|
12
|
-
}
|
|
13
|
-
#nprogress .peg {
|
|
14
|
-
box-shadow: 0 0 10px var(--antd-colorPrimary), 0 0 5px var(--antd-colorPrimary);
|
|
15
|
-
}
|
|
16
|
-
`);
|
|
8
|
+
const deps = getDependencies();
|
|
17
9
|
|
|
18
|
-
router.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
router.afterEach((to, _, err) => {
|
|
11
|
+
const title = to.meta.title;
|
|
12
|
+
|
|
13
|
+
if (!err) {
|
|
14
|
+
document.title = typeof title === 'function'
|
|
15
|
+
? title()
|
|
16
|
+
: title ?? deps.appName;
|
|
17
|
+
}
|
|
23
18
|
});
|
|
24
19
|
}
|
package/admin/styles/index.scss
CHANGED
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
|
|
18
18
|
::-webkit-scrollbar-thumb {
|
|
19
19
|
border-radius: 10px;
|
|
20
|
-
background-color: rgb(
|
|
20
|
+
background-color: rgb(133 133 133 / 20%);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
::-webkit-scrollbar-thumb:hover {
|
|
24
|
-
background-color: rgb(
|
|
24
|
+
background-color: rgb(133 133 133 / 60%);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
package/admin/unocss/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const shortcuts = [
|
|
2
2
|
// 页面板块
|
|
3
|
-
['page-section', 'p-4 bg-$antd-colorBgContainer'],
|
|
3
|
+
['page-section', 'mb-4 p-4 bg-$antd-colorBgContainer'],
|
|
4
4
|
// 用于 FormItem 在 flex布局下的自适应
|
|
5
5
|
[/^fiw-(.*?)_(.*?)_(.*?)$/, ([, w1, w2, w3]) => `min-w-${w1} w-${w2} max-w-${w3} flex-auto`],
|
|
6
|
-
];
|
|
6
|
+
];
|
|
@@ -30,6 +30,7 @@ export function useAntdTable<
|
|
|
30
30
|
current: Number(queryParams.page ?? 1),
|
|
31
31
|
pageSize: Number(queryParams.page_size ?? 10),
|
|
32
32
|
total: pagination?.total ?? 0,
|
|
33
|
+
showTotal: total => `共 ${total} 条`,
|
|
33
34
|
},
|
|
34
35
|
loading: isLoading.value,
|
|
35
36
|
scroll: { x: 'max-content' },
|
package/package.json
CHANGED
package/utils/index.ts
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ref } from 'vue';
|
|
2
|
+
import { createSharedComposable, useMutationObserver } from '@vueuse/core';
|
|
3
|
+
|
|
4
|
+
export { useIsDark };
|
|
5
|
+
|
|
6
|
+
function _useIsDark() {
|
|
7
|
+
const isDarkFn = () => document.documentElement.classList.contains('dark');
|
|
8
|
+
const isDark = ref(isDarkFn());
|
|
9
|
+
|
|
10
|
+
useMutationObserver(
|
|
11
|
+
document.documentElement,
|
|
12
|
+
() => {
|
|
13
|
+
isDark.value = isDarkFn();
|
|
14
|
+
},
|
|
15
|
+
{ attributeFilter: ['class'] },
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
return isDark;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const useIsDark = createSharedComposable(_useIsDark);
|
package/vue/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { useComponentRef } from './hooks/useComponentRef';
|
|
2
2
|
export { useTeleportTarget } from './hooks/useTeleportTarget';
|
|
3
3
|
export { useIsMounted } from './hooks/useIsMounted';
|
|
4
|
+
export { useIsDark } from './hooks/useIsDark';
|
|
4
5
|
export { InfiniteQuery } from './components/infinite-query';
|