@peng_kai/kit 0.0.14 → 0.0.16
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/.vscode/settings.json +41 -0
- package/admin/components/filter/index.ts +5 -0
- package/admin/{filter → components/filter/src}/FilterDrawer.vue +99 -96
- package/admin/{filter → components/filter/src}/FilterParam.vue +76 -76
- package/admin/{filter → components/filter/src}/FilterReset.vue +7 -4
- package/admin/{filter → components/filter/src}/useFilterParams.ts +9 -9
- package/admin/{filter → components/filter/src}/useFilterQuery.ts +11 -11
- package/admin/components/scroll-nav/index.ts +1 -1
- 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 +117 -114
- package/admin/components/text/src/Datetime.vue +44 -44
- package/admin/components/text/src/Duration.vue +26 -26
- package/admin/components/text/src/Hash.vue +42 -40
- package/admin/components/text/src/createTagGetter.ts +13 -13
- package/admin/defines/index.ts +4 -5
- package/admin/defines/page/definePage.ts +12 -0
- package/admin/defines/page/index.ts +1 -0
- package/admin/defines/route/defineRoute.ts +14 -0
- package/admin/defines/route/getRoutes.ts +84 -0
- package/admin/defines/route/helpers.ts +49 -0
- package/admin/defines/route/index.ts +73 -0
- package/admin/defines/route-guard/defineRouteGuard.ts +18 -0
- package/admin/defines/route-guard/getRouteGuards.ts +40 -0
- package/admin/defines/route-guard/index.ts +2 -0
- package/admin/defines/startup/defineStartup.ts +11 -0
- package/admin/defines/startup/getStartups.ts +30 -0
- package/admin/defines/startup/index.ts +2 -0
- package/admin/hooks/index.ts +5 -6
- package/admin/hooks/useMenu.ts +128 -128
- package/admin/hooks/usePage.ts +141 -139
- package/admin/hooks/usePageTab.ts +35 -35
- package/admin/layout/large/Breadcrumb.vue +69 -70
- package/admin/layout/large/Content.vue +24 -24
- package/admin/layout/large/Menu.vue +69 -68
- package/admin/layout/large/PageTab.vue +71 -71
- package/admin/layout/large/index.ts +4 -4
- package/admin/permission/index.ts +4 -0
- package/admin/permission/routerGuard.ts +43 -0
- package/admin/permission/usePermission.ts +52 -0
- package/admin/permission/vuePlugin.ts +30 -0
- package/admin/route-guards/index.ts +2 -0
- package/admin/route-guards/pageProgress.ts +16 -0
- package/admin/route-guards/pageTitle.ts +24 -0
- package/admin/styles/globalCover.scss +43 -43
- package/admin/types/assist.ts +10 -0
- package/admin/unocss/index.ts +1 -1
- package/antd/components/InputNumberRange.vue +53 -47
- package/antd/directives/formLabelAlign.ts +36 -31
- package/antd/hooks/createAntdModal.ts +29 -29
- package/antd/hooks/useAntdDrawer.ts +73 -73
- package/antd/hooks/useAntdForm.helpers.ts +18 -18
- package/antd/hooks/useAntdForm.ts +38 -37
- package/antd/hooks/useAntdModal.ts +25 -25
- package/antd/hooks/useAntdTable.ts +70 -70
- package/antd/hooks/useAntdTheme.ts +86 -0
- package/antd/index.ts +8 -7
- package/eslint.config.js +50 -0
- package/kitDependencies.ts +21 -7
- package/package.json +46 -40
- package/pnpm-lock.yaml +2689 -0
- package/request/helpers.ts +49 -32
- package/request/index.ts +2 -2
- package/request/interceptors/checkCode.ts +8 -8
- package/request/interceptors/filterEmptyValue.ts +9 -9
- package/request/interceptors/formatPaging.ts +12 -12
- package/request/interceptors/index.ts +7 -6
- package/request/interceptors/popupMessage.ts +35 -35
- package/request/interceptors/returnResultType.ts +19 -19
- package/request/interceptors/toLogin.ts +13 -0
- package/request/interceptors/unitizeAxiosError.ts +7 -7
- package/request/queryClient.ts +42 -0
- package/request/request.ts +21 -21
- package/request/type.d.ts +89 -89
- package/tsconfig.json +47 -18
- package/utils/index.ts +67 -29
- package/vue/components/index.ts +1 -0
- package/{components → vue/components}/infinite-query/index.ts +1 -1
- package/{components → vue/components}/infinite-query/src/InfiniteQuery.vue +147 -147
- package/{components → vue/components}/infinite-query/src/useCreateTrigger.ts +35 -35
- package/vue/hooks/useComponentRef.ts +12 -12
- package/vue/hooks/useIsMounted.ts +4 -4
- package/vue/hooks/useTeleportTarget.ts +7 -7
- package/vue/index.ts +4 -3
- package/admin/defines/definePage.ts +0 -14
- package/admin/defines/defineRoute.helpers.ts +0 -30
- package/admin/defines/defineRoute.ts +0 -161
- package/admin/defines/defineRouteGuard.ts +0 -56
- package/admin/defines/defineStartup.ts +0 -41
- package/admin/filter/index.ts +0 -5
- package/admin/hooks/usePermission.ts +0 -5
|
@@ -1,114 +1,117 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { computed } from
|
|
3
|
-
import bigNumber from 'bignumber.js'
|
|
4
|
-
import isNil from 'lodash-es/isNil'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 当 symbol 为以下值时,使用预设的 Logo
|
|
8
|
-
*/
|
|
9
|
-
const presetSymbols: Record<string, string> = {
|
|
10
|
-
USDT: 'https://api.iconify.design/cryptocurrency-color:usdt.svg',
|
|
11
|
-
TRX: 'https://api.iconify.design/cryptocurrency-color:trx.svg',
|
|
12
|
-
USDC: 'https://api.iconify.design/cryptocurrency-color:usdc.svg',
|
|
13
|
-
ETH: 'https://api.iconify.design/cryptocurrency-color:eth.svg',
|
|
14
|
-
BNB: 'https://api.iconify.design/cryptocurrency-color:bnb.svg',
|
|
15
|
-
BUSD: 'https://assets.coingecko.com/coins/images/9576/large/BUSD.png',
|
|
16
|
-
MATIC: 'https://api.iconify.design/cryptocurrency-color:matic.svg',
|
|
17
|
-
SOL: 'https://api.iconify.design/cryptocurrency-color:sol.svg',
|
|
18
|
-
}
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
<script setup lang="ts">
|
|
22
|
-
const props = withDefaults(
|
|
23
|
-
defineProps<{
|
|
24
|
-
/** 金额 */
|
|
25
|
-
amount?: string | number
|
|
26
|
-
/** 符号Logo, 可以是币种Logo的URL、法定货币符号($) */
|
|
27
|
-
symbol?: string
|
|
28
|
-
/** 精度 */
|
|
29
|
-
precision?: number
|
|
30
|
-
/** 单位,如币种名称 */
|
|
31
|
-
unit?: string
|
|
32
|
-
/** 保留小数的位数 */
|
|
33
|
-
fractionDigits?: number
|
|
34
|
-
/** 是否填充 0 */
|
|
35
|
-
padZero?: boolean
|
|
36
|
-
/** 金额是否红/绿显示 */
|
|
37
|
-
colorful?: boolean
|
|
38
|
-
/** 是否是大约数 */
|
|
39
|
-
approx?: boolean
|
|
40
|
-
}>(),
|
|
41
|
-
{
|
|
42
|
-
padZero: false,
|
|
43
|
-
fractionDigits: 18,
|
|
44
|
-
colorful: false,
|
|
45
|
-
},
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
const amountText = computed(() => {
|
|
49
|
-
const _amount = props.amount
|
|
50
|
-
|
|
51
|
-
if (isNil(_amount))
|
|
52
|
-
return '-'
|
|
53
|
-
|
|
54
|
-
let bn = bigNumber(_amount)
|
|
55
|
-
bn = !isNil(props.precision) ? bn.dividedBy(10 ** props.precision) : bn
|
|
56
|
-
let bnt = bn.toFormat(props.fractionDigits)
|
|
57
|
-
bnt = props.padZero ? bnt : bnt.replace(/\.?0+$/, '')
|
|
58
|
-
|
|
59
|
-
return bnt
|
|
60
|
-
})
|
|
61
|
-
const amountColor = computed(() => {
|
|
62
|
-
const num = Number.parseFloat(props.amount as string)
|
|
63
|
-
|
|
64
|
-
if (!props.colorful || (Number.isNaN(num) ? true : num === 0))
|
|
65
|
-
return ''
|
|
66
|
-
|
|
67
|
-
return num > 0 ? '#52c41a' : (num < 0 ? '#ff4d4f' : '#000')
|
|
68
|
-
})
|
|
69
|
-
const symbol = computed(() => presetSymbols[props.symbol!] ?? props.symbol ?? '')
|
|
70
|
-
</script>
|
|
71
|
-
|
|
72
|
-
<template>
|
|
73
|
-
<div class="amount-wrapper" :style="{ '--amount-color': amountColor }">
|
|
74
|
-
<!-- 约等于 -->
|
|
75
|
-
<span v-if="props.approx" class="color-$amount-color">≈</span>
|
|
76
|
-
|
|
77
|
-
<!-- 符号 -->
|
|
78
|
-
<img
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<!--
|
|
82
|
-
<span class="color-$amount-color
|
|
83
|
-
|
|
84
|
-
<!--
|
|
85
|
-
<span
|
|
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
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
import bigNumber from 'bignumber.js';
|
|
4
|
+
import isNil from 'lodash-es/isNil';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 当 symbol 为以下值时,使用预设的 Logo
|
|
8
|
+
*/
|
|
9
|
+
const presetSymbols: Record<string, string> = {
|
|
10
|
+
USDT: 'https://api.iconify.design/cryptocurrency-color:usdt.svg',
|
|
11
|
+
TRX: 'https://api.iconify.design/cryptocurrency-color:trx.svg',
|
|
12
|
+
USDC: 'https://api.iconify.design/cryptocurrency-color:usdc.svg',
|
|
13
|
+
ETH: 'https://api.iconify.design/cryptocurrency-color:eth.svg',
|
|
14
|
+
BNB: 'https://api.iconify.design/cryptocurrency-color:bnb.svg',
|
|
15
|
+
BUSD: 'https://assets.coingecko.com/coins/images/9576/large/BUSD.png',
|
|
16
|
+
MATIC: 'https://api.iconify.design/cryptocurrency-color:matic.svg',
|
|
17
|
+
SOL: 'https://api.iconify.design/cryptocurrency-color:sol.svg',
|
|
18
|
+
};
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script setup lang="ts">
|
|
22
|
+
const props = withDefaults(
|
|
23
|
+
defineProps<{
|
|
24
|
+
/** 金额 */
|
|
25
|
+
amount?: string | number
|
|
26
|
+
/** 符号Logo, 可以是币种Logo的URL、法定货币符号($) */
|
|
27
|
+
symbol?: string
|
|
28
|
+
/** 精度 */
|
|
29
|
+
precision?: number
|
|
30
|
+
/** 单位,如币种名称 */
|
|
31
|
+
unit?: string
|
|
32
|
+
/** 保留小数的位数 */
|
|
33
|
+
fractionDigits?: number
|
|
34
|
+
/** 是否填充 0 */
|
|
35
|
+
padZero?: boolean
|
|
36
|
+
/** 金额是否红/绿显示 */
|
|
37
|
+
colorful?: boolean
|
|
38
|
+
/** 是否是大约数 */
|
|
39
|
+
approx?: boolean
|
|
40
|
+
}>(),
|
|
41
|
+
{
|
|
42
|
+
padZero: false,
|
|
43
|
+
fractionDigits: 18,
|
|
44
|
+
colorful: false,
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const amountText = computed(() => {
|
|
49
|
+
const _amount = props.amount;
|
|
50
|
+
|
|
51
|
+
if (isNil(_amount))
|
|
52
|
+
return '-';
|
|
53
|
+
|
|
54
|
+
let bn = bigNumber(_amount);
|
|
55
|
+
bn = !isNil(props.precision) ? bn.dividedBy(10 ** props.precision) : bn;
|
|
56
|
+
let bnt = bn.toFormat(props.fractionDigits);
|
|
57
|
+
bnt = props.padZero ? bnt : bnt.replace(/\.?0+$/, '');
|
|
58
|
+
|
|
59
|
+
return bnt;
|
|
60
|
+
});
|
|
61
|
+
const amountColor = computed(() => {
|
|
62
|
+
const num = Number.parseFloat(props.amount as string);
|
|
63
|
+
|
|
64
|
+
if (!props.colorful || (Number.isNaN(num) ? true : num === 0))
|
|
65
|
+
return '';
|
|
66
|
+
|
|
67
|
+
return num > 0 ? '#52c41a' : (num < 0 ? '#ff4d4f' : '#000');
|
|
68
|
+
});
|
|
69
|
+
const symbol = computed(() => presetSymbols[props.symbol!] ?? props.symbol ?? '');
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<template>
|
|
73
|
+
<div class="amount-wrapper" :style="{ '--amount-color': amountColor }">
|
|
74
|
+
<!-- 约等于 -->
|
|
75
|
+
<span v-if="props.approx" class="color-$amount-color">≈</span>
|
|
76
|
+
|
|
77
|
+
<!-- 符号 -->
|
|
78
|
+
<img
|
|
79
|
+
v-if="symbol.startsWith('http')" class="symbol-logo" :src="symbol"
|
|
80
|
+
:alt="props.unit"
|
|
81
|
+
> <!-- 图片Logo -->
|
|
82
|
+
<span v-else class="color-$amount-color">{{ symbol }}</span> <!-- 文本Logo,如法定币种符号(¥、$) -->
|
|
83
|
+
|
|
84
|
+
<!-- 金额 -->
|
|
85
|
+
<span class="color-$amount-color amount">{{ amountText }}</span>
|
|
86
|
+
|
|
87
|
+
<!-- 单位 -->
|
|
88
|
+
<span v-if="props.unit" class="unit">{{ props.unit }}</span>
|
|
89
|
+
</div>
|
|
90
|
+
</template>
|
|
91
|
+
|
|
92
|
+
<style lang="scss">
|
|
93
|
+
.amount-wrapper {
|
|
94
|
+
display: flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
|
|
97
|
+
.symbol-logo {
|
|
98
|
+
display: block;
|
|
99
|
+
width: 1.1em;
|
|
100
|
+
height: 1.1em;
|
|
101
|
+
margin-right: 0.2em;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.amount {
|
|
105
|
+
font-family: 'dinm';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.currency-name {
|
|
109
|
+
margin-left: 0.2em;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.unit {
|
|
113
|
+
display: inline-block;
|
|
114
|
+
margin-left: 0.2em;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
</style>
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { Tooltip as ATooltip } from
|
|
3
|
-
import { computed } from
|
|
4
|
-
import { getDependencies } from
|
|
5
|
-
|
|
6
|
-
defineOptions({
|
|
7
|
-
inheritAttrs: false,
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
const props = withDefaults(
|
|
11
|
-
defineProps<{
|
|
12
|
-
timestamp?: number | string
|
|
13
|
-
template?: string
|
|
14
|
-
}>(),
|
|
15
|
-
{
|
|
16
|
-
template: 'MM-DD HH:mm:ss',
|
|
17
|
-
},
|
|
18
|
-
)
|
|
19
|
-
const {dayjs} = getDependencies()
|
|
20
|
-
const timestamp = computed(() => {
|
|
21
|
-
let tsStr = String(props.timestamp)
|
|
22
|
-
|
|
23
|
-
if (tsStr.length === 10)
|
|
24
|
-
tsStr += '000'
|
|
25
|
-
if (tsStr.length !== 13)
|
|
26
|
-
return
|
|
27
|
-
|
|
28
|
-
const tsNum = Number.parseInt(tsStr)
|
|
29
|
-
|
|
30
|
-
return Number.isNaN(tsNum) ? undefined : tsNum
|
|
31
|
-
})
|
|
32
|
-
</script>
|
|
33
|
-
|
|
34
|
-
<template>
|
|
35
|
-
<ATooltip destroyTooltipOnHide>
|
|
36
|
-
<template v-if="timestamp" #title>
|
|
37
|
-
<div>{{ dayjs(timestamp).fromNow?.() }}</div>
|
|
38
|
-
<div>{{ dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss') }}</div>
|
|
39
|
-
</template>
|
|
40
|
-
<span v-bind="$attrs">{{ timestamp ? dayjs(timestamp).format(props.template) : '-' }}</span>
|
|
41
|
-
</ATooltip>
|
|
42
|
-
</template>
|
|
43
|
-
|
|
44
|
-
<style scoped lang="scss"></style>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Tooltip as ATooltip } from 'ant-design-vue';
|
|
3
|
+
import { computed } from 'vue';
|
|
4
|
+
import { getDependencies } from '../../../../kitDependencies';
|
|
5
|
+
|
|
6
|
+
defineOptions({
|
|
7
|
+
inheritAttrs: false,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(
|
|
11
|
+
defineProps<{
|
|
12
|
+
timestamp?: number | string
|
|
13
|
+
template?: string
|
|
14
|
+
}>(),
|
|
15
|
+
{
|
|
16
|
+
template: 'MM-DD HH:mm:ss',
|
|
17
|
+
},
|
|
18
|
+
);
|
|
19
|
+
const { dayjs } = getDependencies()!;
|
|
20
|
+
const timestamp = computed(() => {
|
|
21
|
+
let tsStr = String(props.timestamp);
|
|
22
|
+
|
|
23
|
+
if (tsStr.length === 10)
|
|
24
|
+
tsStr += '000';
|
|
25
|
+
if (tsStr.length !== 13)
|
|
26
|
+
return;
|
|
27
|
+
|
|
28
|
+
const tsNum = Number.parseInt(tsStr);
|
|
29
|
+
|
|
30
|
+
return Number.isNaN(tsNum) ? undefined : tsNum;
|
|
31
|
+
});
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<ATooltip destroyTooltipOnHide>
|
|
36
|
+
<template v-if="timestamp" #title>
|
|
37
|
+
<div>{{ dayjs(timestamp).fromNow?.() }}</div>
|
|
38
|
+
<div>{{ dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss') }}</div>
|
|
39
|
+
</template>
|
|
40
|
+
<span v-bind="$attrs">{{ timestamp ? dayjs(timestamp).format(props.template) : '-' }}</span>
|
|
41
|
+
</ATooltip>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<style scoped lang="scss"></style>
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed } from
|
|
3
|
-
|
|
4
|
-
const props = defineProps<{
|
|
5
|
-
seconds: number
|
|
6
|
-
}>()
|
|
7
|
-
|
|
8
|
-
const formattedDuration = computed(() => {
|
|
9
|
-
const days = Math.floor(props.seconds / (3600 * 24))
|
|
10
|
-
const hours = Math.floor((props.seconds % (3600 * 24)) / 3600)
|
|
11
|
-
const minutes = Math.floor((props.seconds % 3600) / 60)
|
|
12
|
-
const seconds = props.seconds % 60
|
|
13
|
-
let formattedDuration = ''
|
|
14
|
-
|
|
15
|
-
days >= 1 && (formattedDuration += `${Math.floor(days)}天 `)
|
|
16
|
-
hours >= 1 && (formattedDuration += `${Math.floor(hours)}小时 `)
|
|
17
|
-
minutes >= 1 && (formattedDuration += `${Math.floor(minutes)}分钟 `)
|
|
18
|
-
seconds >= 1 && (formattedDuration += `${Math.floor(seconds)}秒`)
|
|
19
|
-
|
|
20
|
-
return formattedDuration
|
|
21
|
-
})
|
|
22
|
-
</script>
|
|
23
|
-
|
|
24
|
-
<template>
|
|
25
|
-
<span>{{ formattedDuration }}</span>
|
|
26
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
seconds: number
|
|
6
|
+
}>();
|
|
7
|
+
|
|
8
|
+
const formattedDuration = computed(() => {
|
|
9
|
+
const days = Math.floor(props.seconds / (3600 * 24));
|
|
10
|
+
const hours = Math.floor((props.seconds % (3600 * 24)) / 3600);
|
|
11
|
+
const minutes = Math.floor((props.seconds % 3600) / 60);
|
|
12
|
+
const seconds = props.seconds % 60;
|
|
13
|
+
let formattedDuration = '';
|
|
14
|
+
|
|
15
|
+
days >= 1 && (formattedDuration += `${Math.floor(days)}天 `);
|
|
16
|
+
hours >= 1 && (formattedDuration += `${Math.floor(hours)}小时 `);
|
|
17
|
+
minutes >= 1 && (formattedDuration += `${Math.floor(minutes)}分钟 `);
|
|
18
|
+
seconds >= 1 && (formattedDuration += `${Math.floor(seconds)}秒`);
|
|
19
|
+
|
|
20
|
+
return formattedDuration;
|
|
21
|
+
});
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<span>{{ formattedDuration }}</span>
|
|
26
|
+
</template>
|
|
@@ -1,40 +1,42 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { Tooltip as ATooltip, TypographyLink as ATypographyLink } from
|
|
3
|
-
import { computed } from
|
|
4
|
-
import { desensitize, getScanBrowser } from '../../../../utils'
|
|
5
|
-
|
|
6
|
-
type HashType = Parameters<typeof getScanBrowser>[2]
|
|
7
|
-
|
|
8
|
-
defineOptions({
|
|
9
|
-
inheritAttrs: false,
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
const props = withDefaults(
|
|
13
|
-
defineProps<{
|
|
14
|
-
hash: string
|
|
15
|
-
hide?: boolean
|
|
16
|
-
chain?: string
|
|
17
|
-
type?: HashType
|
|
18
|
-
}>(),
|
|
19
|
-
{
|
|
20
|
-
hide: true,
|
|
21
|
-
},
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
const href = computed(() => {
|
|
25
|
-
const { hash, chain, type } = props
|
|
26
|
-
|
|
27
|
-
if (!hash || !chain || !type)
|
|
28
|
-
return
|
|
29
|
-
return getScanBrowser(chain, hash, type)
|
|
30
|
-
})
|
|
31
|
-
</script>
|
|
32
|
-
|
|
33
|
-
<template>
|
|
34
|
-
<ATypographyLink :href="href" :copyable="{ text: props.hash, tooltip: false }" target="_blank">
|
|
35
|
-
<ATooltip>
|
|
36
|
-
<template v-if="props.hide" #title>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Tooltip as ATooltip, TypographyLink as ATypographyLink } from 'ant-design-vue';
|
|
3
|
+
import { computed } from 'vue';
|
|
4
|
+
import { desensitize, getScanBrowser } from '../../../../utils';
|
|
5
|
+
|
|
6
|
+
type HashType = Parameters<typeof getScanBrowser>[2];
|
|
7
|
+
|
|
8
|
+
defineOptions({
|
|
9
|
+
inheritAttrs: false,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(
|
|
13
|
+
defineProps<{
|
|
14
|
+
hash: string
|
|
15
|
+
hide?: boolean
|
|
16
|
+
chain?: string
|
|
17
|
+
type?: HashType
|
|
18
|
+
}>(),
|
|
19
|
+
{
|
|
20
|
+
hide: true,
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const href = computed(() => {
|
|
25
|
+
const { hash, chain, type } = props;
|
|
26
|
+
|
|
27
|
+
if (!hash || !chain || !type)
|
|
28
|
+
return;
|
|
29
|
+
return getScanBrowser(chain, hash, type);
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<ATypographyLink :href="href" :copyable="{ text: props.hash, tooltip: false }" target="_blank">
|
|
35
|
+
<ATooltip>
|
|
36
|
+
<template v-if="props.hide" #title>
|
|
37
|
+
{{ props.hash }}
|
|
38
|
+
</template>
|
|
39
|
+
<span v-bind="$attrs">{{ props.hide ? desensitize(props.hash) : props.hash }}</span>
|
|
40
|
+
</ATooltip>
|
|
41
|
+
</ATypographyLink>
|
|
42
|
+
</template>
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { computed, h } from
|
|
2
|
-
import { Tag as ATag } from 'ant-design-vue'
|
|
3
|
-
import type { TagProps } from 'ant-design-vue'
|
|
4
|
-
|
|
5
|
-
export function createTagGetter(typeMapFn: () => { [code: number | string]: [ text: string, color: TagProps['color'] ] }) {
|
|
6
|
-
const typeMap = computed(typeMapFn)
|
|
7
|
-
|
|
8
|
-
return (type: number) => {
|
|
9
|
-
const [text, color] = typeMap.value[type] ?? []
|
|
10
|
-
|
|
11
|
-
return text ? h(ATag, { color }, () => text) : h('span', null, '-')
|
|
12
|
-
}
|
|
13
|
-
}
|
|
1
|
+
import { computed, h } from 'vue';
|
|
2
|
+
import { Tag as ATag } from 'ant-design-vue';
|
|
3
|
+
import type { TagProps } from 'ant-design-vue';
|
|
4
|
+
|
|
5
|
+
export function createTagGetter(typeMapFn: () => { [code: number | string]: [ text: string, color: TagProps['color'] ] }) {
|
|
6
|
+
const typeMap = computed(typeMapFn);
|
|
7
|
+
|
|
8
|
+
return (type: number) => {
|
|
9
|
+
const [text, color] = typeMap.value[type] ?? [];
|
|
10
|
+
|
|
11
|
+
return text ? h(ATag, { color }, () => text) : h('span', null, '-');
|
|
12
|
+
};
|
|
13
|
+
}
|
package/admin/defines/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export { definePage } from './
|
|
2
|
-
export { defineRoute, getRoutes } from './
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export { defineStartup, getStartups } from './defineStartup'
|
|
1
|
+
export { definePage } from './page';
|
|
2
|
+
export { defineRoute, getRoutes } from './route';
|
|
3
|
+
export { defineRouteGuard, getRouteGuards } from './route-guard';
|
|
4
|
+
export { defineStartup, getStartups } from './startup';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineAsyncComponent, defineComponent, h } from 'vue';
|
|
2
|
+
import type { AsyncComponentLoader } from 'vue';
|
|
3
|
+
|
|
4
|
+
export function definePage(loader: AsyncComponentLoader) {
|
|
5
|
+
return defineComponent({
|
|
6
|
+
setup() {
|
|
7
|
+
const Page = defineAsyncComponent(loader);
|
|
8
|
+
|
|
9
|
+
return () => h(Page, null);
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { definePage } from './definePage';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RouteRecordRaw } from 'vue-router';
|
|
2
|
+
import { definePage } from '../page';
|
|
3
|
+
|
|
4
|
+
export { defineRoute, RouteSymbol };
|
|
5
|
+
|
|
6
|
+
const RouteSymbol = Symbol('app-route');
|
|
7
|
+
|
|
8
|
+
/** 定义路由 */
|
|
9
|
+
function defineRoute(route: (params: { definePage: typeof definePage }) => RouteRecordRaw[]) {
|
|
10
|
+
const routeFn: any = () => route({ definePage });
|
|
11
|
+
routeFn[RouteSymbol] = true;
|
|
12
|
+
|
|
13
|
+
return routeFn;
|
|
14
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { RouteRecordRaw } from 'vue-router';
|
|
2
|
+
import merge from 'lodash-es/merge';
|
|
3
|
+
import { ENV } from '../../../utils';
|
|
4
|
+
import { RouteSymbol } from './defineRoute';
|
|
5
|
+
|
|
6
|
+
export { getRoutes };
|
|
7
|
+
|
|
8
|
+
/** 获取路由 */
|
|
9
|
+
async function getRoutes() {
|
|
10
|
+
const routeFileRE = /\/([A-Za-z0-9-]+.)?route\.ts$/;
|
|
11
|
+
const routeFiles = Object.fromEntries(
|
|
12
|
+
Object.entries(getRoutes.modules).filter(([n]) => routeFileRE.test(n)),
|
|
13
|
+
) as Record<string, Function>;
|
|
14
|
+
let routes: RouteRecordRaw[] = [];
|
|
15
|
+
|
|
16
|
+
for (const name in routeFiles) {
|
|
17
|
+
const module: any = await routeFiles[name]();
|
|
18
|
+
|
|
19
|
+
if (Object.hasOwn(module?.default ?? {}, RouteSymbol))
|
|
20
|
+
Array.prototype.push.apply(routes, module.default());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 处理路由
|
|
24
|
+
routes = meregeDefaultRouteParams(routes);
|
|
25
|
+
routes = sortRoute(routes);
|
|
26
|
+
|
|
27
|
+
// 输出路由
|
|
28
|
+
if (!ENV.isProd) {
|
|
29
|
+
console.groupCollapsed('一级路由');
|
|
30
|
+
console.table(
|
|
31
|
+
routes.map(route => ({ order: route.meta?.order, ...route })),
|
|
32
|
+
['order', 'name', 'path'],
|
|
33
|
+
);
|
|
34
|
+
console.groupEnd();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return routes;
|
|
38
|
+
}
|
|
39
|
+
getRoutes.modules = {} as any;
|
|
40
|
+
|
|
41
|
+
/** 给路由填充默认参数 */
|
|
42
|
+
const defaultRouteParams: Partial<RouteRecordRaw> = {
|
|
43
|
+
meta: {
|
|
44
|
+
title: '标题',
|
|
45
|
+
icon: undefined,
|
|
46
|
+
order: 10,
|
|
47
|
+
requireAuth: true,
|
|
48
|
+
keepAlive: false,
|
|
49
|
+
hiddenTab: false,
|
|
50
|
+
pageKeyFn: route => route.path,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function meregeDefaultRouteParams(routes: RouteRecordRaw[], parentRoute?: RouteRecordRaw) {
|
|
55
|
+
const _routes: typeof routes = [];
|
|
56
|
+
|
|
57
|
+
for (const route of routes) {
|
|
58
|
+
if (route.children?.length)
|
|
59
|
+
route.children = meregeDefaultRouteParams(route.children, route);
|
|
60
|
+
|
|
61
|
+
const newRoute = merge({}, defaultRouteParams, route);
|
|
62
|
+
const { menuOrder } = newRoute.meta!;
|
|
63
|
+
|
|
64
|
+
// 处理 menuOrder
|
|
65
|
+
if (typeof menuOrder === 'string')
|
|
66
|
+
newRoute.meta!.menuOrder = menuOrder.replace('..', (parentRoute?.name ?? '') as string);
|
|
67
|
+
|
|
68
|
+
_routes.push(newRoute);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return _routes;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** 给路由排序 */
|
|
75
|
+
function sortRoute(routes: RouteRecordRaw[]) {
|
|
76
|
+
const _routes = routes.sort((r1, r2) => Number(r2?.meta?.order) - Number(r1?.meta?.order));
|
|
77
|
+
|
|
78
|
+
for (const route of _routes) {
|
|
79
|
+
if (route.children?.length)
|
|
80
|
+
route.children = sortRoute(route.children);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return _routes;
|
|
84
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { RouteLocationNormalizedLoaded, Router } from 'vue-router';
|
|
2
|
+
|
|
3
|
+
export function getTitle(route?: Pick<RouteLocationNormalizedLoaded, 'meta'>) {
|
|
4
|
+
const mTitle = route?.meta?.title;
|
|
5
|
+
|
|
6
|
+
return typeof mTitle === 'function' ? mTitle() : mTitle;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getMenusByRouter(router: Router) {
|
|
10
|
+
const menuOrderRE = /^(?<key>\w*)@(?<order>\d+)$/;
|
|
11
|
+
const routes = router.getRoutes();
|
|
12
|
+
const menus = routes
|
|
13
|
+
.filter(route => menuOrderRE.test((route.meta.menuOrder ?? '')))
|
|
14
|
+
.map((route) => {
|
|
15
|
+
const res = route.meta!.menuOrder!.match(menuOrderRE);
|
|
16
|
+
const parentKey = res?.groups?.key;
|
|
17
|
+
const order = Number(res?.groups?.order);
|
|
18
|
+
const name = route.name as string;
|
|
19
|
+
const menu = {
|
|
20
|
+
key: name,
|
|
21
|
+
label: route.meta.title ?? name,
|
|
22
|
+
icon: route.meta.icon,
|
|
23
|
+
trigger: () => router.push({ name }),
|
|
24
|
+
order,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return { parentKey, menu };
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// 将含有子菜单的菜单的 trigger 设置为空函数
|
|
31
|
+
const hasSubMenus = menus.filter(menu => menus.some(m => m.parentKey === menu.menu.key));
|
|
32
|
+
hasSubMenus.forEach((menu) => {
|
|
33
|
+
menu.menu.trigger = (() => {}) as any;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return menus;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function printRounesNameInterface(routes: { name: PropertyKey }[]) {
|
|
40
|
+
console.groupCollapsed('路由命名');
|
|
41
|
+
console.log('复制以下内容到 AppRouteNames');
|
|
42
|
+
const routesName = new Set();
|
|
43
|
+
routes.forEach((route) => {
|
|
44
|
+
if (typeof route.name === 'string')
|
|
45
|
+
routesName.add(route.name);
|
|
46
|
+
});
|
|
47
|
+
console.log([...routesName.values()].sort().filter(name => !!name).map(name => `${name}: true`).join('\n'));
|
|
48
|
+
console.groupEnd();
|
|
49
|
+
}
|