@peng_kai/kit 0.1.17 → 0.2.0-beta.1
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/adminPlugin.ts +47 -0
- package/admin/components/filter/src/FilterDrawer.vue +153 -153
- package/admin/components/filter/src/FilterParam.vue +76 -78
- package/admin/components/filter/src/FilterReset.vue +2 -2
- package/admin/components/filter/src/useFilterParams.ts +75 -25
- package/admin/components/filter/src/useFilterQuery.ts +32 -16
- package/admin/components/rich-text/index.ts +2 -0
- package/admin/components/rich-text/src/RichText.vue +342 -0
- package/admin/components/rich-text/src/imageUploader.ts +34 -0
- package/admin/components/rich-text/src/type.d.ts +7 -0
- 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 +121 -121
- package/admin/components/text/src/Datetime.vue +47 -48
- package/admin/components/text/src/Duration.vue +26 -26
- package/admin/components/text/src/Hash.vue +51 -51
- package/admin/components/text/src/createTagGetter.ts +13 -13
- package/admin/components/upload/index.ts +2 -0
- package/admin/components/upload/src/PictureCardUpload.vue +143 -0
- package/admin/components/upload/src/customRequests.ts +31 -0
- package/admin/defines/index.ts +1 -1
- package/admin/defines/route/helpers.ts +0 -1
- package/admin/defines/startup/defineStartup.ts +8 -1
- package/admin/defines/startup/index.ts +1 -1
- package/admin/defines/startup/{getStartups.ts → runStartup.ts} +16 -7
- package/admin/layout/large/Breadcrumb.vue +68 -69
- package/admin/layout/large/Content.vue +23 -24
- package/admin/layout/large/Menu.vue +68 -69
- package/admin/layout/large/PageTab.vue +70 -71
- package/admin/permission/index.ts +2 -4
- package/admin/permission/routerGuard.ts +41 -43
- package/admin/permission/vuePlugin.ts +46 -30
- package/admin/route-guards/collapseMenu.ts +3 -3
- package/admin/route-guards/index.ts +3 -3
- package/admin/route-guards/pageProgress.ts +27 -27
- package/admin/route-guards/pageTitle.ts +18 -19
- package/admin/{hooks/useMenu.ts → stores/createUseMenuStore.ts} +133 -128
- package/admin/{hooks/usePage.ts → stores/createUsePageStore.ts} +145 -141
- package/admin/stores/createUsePageTabStore.ts +43 -0
- package/admin/{permission/usePermission.ts → stores/createUsePermissionStore.ts} +57 -52
- package/admin/stores/index.ts +8 -0
- package/admin/styles/classCover.scss +8 -0
- package/admin/styles/globalCover.scss +54 -54
- package/antd/components/InputNumberRange.vue +59 -59
- package/antd/directives/formLabelAlign.ts +36 -36
- package/antd/hooks/useAntdDrawer.ts +73 -73
- package/antd/hooks/useAntdForm.helpers.ts +92 -8
- package/antd/hooks/useAntdForm.ts +55 -63
- package/antd/hooks/useAntdTable.ts +127 -115
- package/antd/index.ts +1 -1
- package/libs/a-calc.ts +1 -0
- package/libs/axios.ts +2 -0
- package/libs/bignumber.ts +2 -0
- package/libs/dayjs.ts +5 -0
- package/libs/echarts.ts +5 -0
- package/libs/localstorage-slim.ts +2 -0
- package/libs/lodash-es.ts +1 -0
- package/libs/pinia.ts +1 -0
- package/libs/vue-query.ts +1 -0
- package/libs/vueuse.ts +3 -0
- package/package.json +91 -58
- package/request/helpers.ts +68 -49
- package/request/interceptors/toLogin.ts +26 -26
- package/request/queryClient.ts +34 -21
- package/request/type.d.ts +92 -92
- package/stylelint.config.cjs +7 -7
- package/tsconfig.json +50 -50
- package/utils/upload/AwsS3.ts +68 -0
- package/utils/upload/fileHandlers.ts +27 -0
- package/utils/upload/index.ts +2 -0
- package/vite/index.d.ts +1 -0
- package/vite/index.mjs +27 -0
- package/vue/components/echarts/index.ts +1 -0
- package/vue/components/echarts/src/ECharts.vue +48 -0
- package/vue/components/index.ts +1 -0
- package/vue/components/infinite-query/index.ts +1 -1
- package/vue/components/infinite-query/src/InfiniteQuery.vue +199 -205
- package/vue/components/infinite-query/src/useCreateTrigger.ts +39 -39
- package/vue/components/test/KitTest.vue +9 -0
- package/vue/components/test/testStore.ts +11 -0
- package/admin/hooks/index.ts +0 -5
- package/admin/hooks/usePageTab.ts +0 -35
- package/kitDependencies.ts +0 -43
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { App } from 'vue';
|
|
2
|
+
import type { Router } from 'vue-router';
|
|
3
|
+
import type { TUseMenuStore } from './stores/createUseMenuStore';
|
|
4
|
+
import type { TUsePageStore } from './stores/createUsePageStore';
|
|
5
|
+
import type { TUsePageTabStore } from './stores/createUsePageTabStore';
|
|
6
|
+
import type { TRole, TUsePermissionStore } from './stores/createUsePermissionStore';
|
|
7
|
+
|
|
8
|
+
interface IOtions {
|
|
9
|
+
meta: {
|
|
10
|
+
appId: string
|
|
11
|
+
appName: string
|
|
12
|
+
}
|
|
13
|
+
deps: {
|
|
14
|
+
router: Router
|
|
15
|
+
roles: Record<string, TRole>
|
|
16
|
+
useMenuStore: TUseMenuStore
|
|
17
|
+
usePageStore: TUsePageStore
|
|
18
|
+
usePageTabStore: TUsePageTabStore
|
|
19
|
+
usePermissionStore: TUsePermissionStore
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let _meta: IOtions['meta'];
|
|
24
|
+
let _deps: IOtions['deps'];
|
|
25
|
+
|
|
26
|
+
export const adminPlugin = {
|
|
27
|
+
install(_app: App, options: IOtions) {
|
|
28
|
+
_meta = options.meta;
|
|
29
|
+
_deps = options.deps;
|
|
30
|
+
},
|
|
31
|
+
meta: new Proxy({} as IOtions['meta'], {
|
|
32
|
+
get(_, p) {
|
|
33
|
+
return _meta?.[p];
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
deps: new Proxy({} as IOtions['deps'], {
|
|
37
|
+
get(_, p) {
|
|
38
|
+
const _p = String(p);
|
|
39
|
+
const _dep = _deps?.[p];
|
|
40
|
+
|
|
41
|
+
if (!_dep)
|
|
42
|
+
throw new Error(`${_p} 不在 kit 依赖中,请通过 kitPlugin 传入 ${_p}`);
|
|
43
|
+
|
|
44
|
+
return _dep;
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
@@ -1,153 +1,153 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { computed, ref } from 'vue';
|
|
3
|
-
import { Button as AButton, Drawer as ADrawer, Dropdown as ADropdown } from 'ant-design-vue';
|
|
4
|
-
|
|
5
|
-
defineOptions({
|
|
6
|
-
inheritAttrs: false,
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
const props = withDefaults(defineProps<{
|
|
10
|
-
loading?: boolean
|
|
11
|
-
filterQuery?: any
|
|
12
|
-
filterParams?: any
|
|
13
|
-
filterForm?: any
|
|
14
|
-
}>(), {
|
|
15
|
-
loading: undefined,
|
|
16
|
-
});
|
|
17
|
-
const emits = defineEmits<{
|
|
18
|
-
(e: 'filter'): void
|
|
19
|
-
(e: 'reset', value: number): void
|
|
20
|
-
}>();
|
|
21
|
-
|
|
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
|
-
});
|
|
35
|
-
|
|
36
|
-
function filter() {
|
|
37
|
-
props.filterParams?.update?.();
|
|
38
|
-
emits('filter');
|
|
39
|
-
filterVisible.value = false;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function reset() {
|
|
43
|
-
props.filterForm?.$form.resetFields?.();
|
|
44
|
-
props.filterParams?.update?.();
|
|
45
|
-
emits('reset', 1);
|
|
46
|
-
filterVisible.value = false;
|
|
47
|
-
}
|
|
48
|
-
</script>
|
|
49
|
-
|
|
50
|
-
<template>
|
|
51
|
-
<div class="p-3 bg-$antd-colorBgContainer text-14px rounded-2" v-bind="$attrs">
|
|
52
|
-
<!-- .filter-params 为空时显示 .filter-params-tips -->
|
|
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>
|
|
61
|
-
</div>
|
|
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>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
|
|
78
|
-
<ADrawer
|
|
79
|
-
v-model:open="filterVisible" class="filter-drawer" placement="bottom"
|
|
80
|
-
height="50vh"
|
|
81
|
-
>
|
|
82
|
-
<template #extra>
|
|
83
|
-
<AButton class="mr-3 my--3" :disabled="loading" @click="reset()">
|
|
84
|
-
重置
|
|
85
|
-
</AButton>
|
|
86
|
-
<AButton class="my--3" type="primary" :loading="loading" @click="filter()">
|
|
87
|
-
筛选
|
|
88
|
-
</AButton>
|
|
89
|
-
</template>
|
|
90
|
-
<template #default>
|
|
91
|
-
<slot />
|
|
92
|
-
</template>
|
|
93
|
-
</ADrawer>
|
|
94
|
-
</template>
|
|
95
|
-
|
|
96
|
-
<style scoped lang="scss">
|
|
97
|
-
.filter-params {
|
|
98
|
-
display: flex;
|
|
99
|
-
flex-wrap: wrap;
|
|
100
|
-
justify-content: flex-start;
|
|
101
|
-
gap: 5px 15px;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// .filter-params 为空时显示 .filter-params-tips
|
|
105
|
-
.filter-params-tips {
|
|
106
|
-
display: none;
|
|
107
|
-
color: theme('colors.gray.DEFAULT');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.filter-params:empty {
|
|
111
|
-
display: none;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.filter-params:empty + .filter-params-tips {
|
|
115
|
-
display: block;
|
|
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
|
-
}
|
|
126
|
-
</style>
|
|
127
|
-
|
|
128
|
-
<style lang="scss">
|
|
129
|
-
.filter-drawer {
|
|
130
|
-
.ant-drawer-header {
|
|
131
|
-
padding: 16px;
|
|
132
|
-
|
|
133
|
-
.ant-drawer-close {
|
|
134
|
-
--expand: 5px;
|
|
135
|
-
|
|
136
|
-
padding: var(--expand);
|
|
137
|
-
margin: calc(var(--expand) * -1) var(--expand) calc(var(--expand) * -1) 0;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.ant-drawer-body {
|
|
142
|
-
padding: 16px;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.ant-form-item {
|
|
146
|
-
margin-bottom: 0;
|
|
147
|
-
|
|
148
|
-
.ant-form-item-label {
|
|
149
|
-
padding-bottom: 0;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
</style>
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, ref } from 'vue';
|
|
3
|
+
import { Button as AButton, Drawer as ADrawer, Dropdown as ADropdown } from 'ant-design-vue';
|
|
4
|
+
|
|
5
|
+
defineOptions({
|
|
6
|
+
inheritAttrs: false,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(defineProps<{
|
|
10
|
+
loading?: boolean
|
|
11
|
+
filterQuery?: any
|
|
12
|
+
filterParams?: any
|
|
13
|
+
filterForm?: any
|
|
14
|
+
}>(), {
|
|
15
|
+
loading: undefined,
|
|
16
|
+
});
|
|
17
|
+
const emits = defineEmits<{
|
|
18
|
+
(e: 'filter'): void
|
|
19
|
+
(e: 'reset', value: number): void
|
|
20
|
+
}>();
|
|
21
|
+
|
|
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
|
+
});
|
|
35
|
+
|
|
36
|
+
function filter() {
|
|
37
|
+
props.filterParams?.update?.();
|
|
38
|
+
emits('filter');
|
|
39
|
+
filterVisible.value = false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function reset() {
|
|
43
|
+
props.filterForm?.$form.resetFields?.();
|
|
44
|
+
props.filterParams?.update?.();
|
|
45
|
+
emits('reset', 1);
|
|
46
|
+
filterVisible.value = false;
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<div class="p-3 bg-$antd-colorBgContainer text-14px rounded-2" v-bind="$attrs">
|
|
52
|
+
<!-- .filter-params 为空时显示 .filter-params-tips -->
|
|
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>
|
|
61
|
+
</div>
|
|
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>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<ADrawer
|
|
79
|
+
v-model:open="filterVisible" class="filter-drawer" placement="bottom"
|
|
80
|
+
height="50vh"
|
|
81
|
+
>
|
|
82
|
+
<template #extra>
|
|
83
|
+
<AButton class="mr-3 my--3" :disabled="loading" @click="reset()">
|
|
84
|
+
重置
|
|
85
|
+
</AButton>
|
|
86
|
+
<AButton class="my--3" type="primary" :loading="loading" @click="filter()">
|
|
87
|
+
筛选
|
|
88
|
+
</AButton>
|
|
89
|
+
</template>
|
|
90
|
+
<template #default>
|
|
91
|
+
<slot />
|
|
92
|
+
</template>
|
|
93
|
+
</ADrawer>
|
|
94
|
+
</template>
|
|
95
|
+
|
|
96
|
+
<style scoped lang="scss">
|
|
97
|
+
.filter-params {
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-wrap: wrap;
|
|
100
|
+
justify-content: flex-start;
|
|
101
|
+
gap: 5px 15px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// .filter-params 为空时显示 .filter-params-tips
|
|
105
|
+
.filter-params-tips {
|
|
106
|
+
display: none;
|
|
107
|
+
color: theme('colors.gray.DEFAULT');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.filter-params:empty {
|
|
111
|
+
display: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.filter-params:empty + .filter-params-tips {
|
|
115
|
+
display: block;
|
|
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
|
+
}
|
|
126
|
+
</style>
|
|
127
|
+
|
|
128
|
+
<style lang="scss">
|
|
129
|
+
.filter-drawer {
|
|
130
|
+
.ant-drawer-header {
|
|
131
|
+
padding: 16px;
|
|
132
|
+
|
|
133
|
+
.ant-drawer-close {
|
|
134
|
+
--expand: 5px;
|
|
135
|
+
|
|
136
|
+
padding: var(--expand);
|
|
137
|
+
margin: calc(var(--expand) * -1) var(--expand) calc(var(--expand) * -1) 0;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.ant-drawer-body {
|
|
142
|
+
padding: 16px;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.ant-form-item {
|
|
146
|
+
margin-bottom: 0;
|
|
147
|
+
|
|
148
|
+
.ant-form-item-label {
|
|
149
|
+
padding-bottom: 0;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
</style>
|
|
@@ -1,78 +1,76 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import isNil from 'lodash-es/isNil';
|
|
3
|
-
import isFinite from 'lodash-es/isFinite';
|
|
4
|
-
import bignumber from 'bignumber.js';
|
|
5
|
-
import {
|
|
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?: [any, any], template = 'YYYY-MM-DD') {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
return;
|
|
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
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import isNil from 'lodash-es/isNil';
|
|
3
|
+
import isFinite from 'lodash-es/isFinite';
|
|
4
|
+
import bignumber from 'bignumber.js';
|
|
5
|
+
import { dayjs } from '../../../../utils/date';
|
|
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?: [any, any], 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>
|
|
@@ -29,13 +29,13 @@ const loading = computed(() => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
function filter() {
|
|
32
|
-
props.filterParams?.update?.(
|
|
32
|
+
props.filterParams?.update?.(true);
|
|
33
33
|
emits('filter');
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function reset() {
|
|
37
37
|
props.filterForm?.$form.resetFields?.();
|
|
38
|
-
props.filterParams?.update?.(
|
|
38
|
+
props.filterParams?.update?.(true);
|
|
39
39
|
emits('reset');
|
|
40
40
|
}
|
|
41
41
|
</script>
|
|
@@ -1,28 +1,78 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { reactive } from 'vue';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
import { cloneDeep, eq, mapValues } from 'lodash-es';
|
|
2
|
+
import { type DeepReadonly, type MaybeRefOrGetter, reactive, toRef, watch } from 'vue';
|
|
3
|
+
|
|
4
|
+
export type ShcemeConfig<T> = {
|
|
5
|
+
[P in keyof T]: {
|
|
6
|
+
/**
|
|
7
|
+
* 属性值。可以传入 普通值、Ref对象、一个函数(get函数,只读)
|
|
8
|
+
*/
|
|
9
|
+
value: MaybeRefOrGetter<T[P]>
|
|
10
|
+
/**
|
|
11
|
+
* 是否是响应式,默认 `false`
|
|
12
|
+
* - `true`: 值的变化立即生效到 `$params`
|
|
13
|
+
* - `false`: 值不会立即生效到 `$params`,需要调用 `update`
|
|
14
|
+
*/
|
|
15
|
+
responsive?: boolean
|
|
16
|
+
/**
|
|
17
|
+
* 当值变化时是否重置 `page`,默认 `true`
|
|
18
|
+
*/
|
|
19
|
+
resetPage?: boolean
|
|
20
|
+
/**
|
|
21
|
+
* 在值变化后,生效到 `$params`前,触发 `before` 钩子
|
|
22
|
+
* @param bp 所有在 `before` 函数中被修改的属性组成的对象
|
|
23
|
+
* @param np 触发 `before` 函数时的 params 对象
|
|
24
|
+
*/
|
|
25
|
+
before?(bp: T, np: DeepReadonly<T>): void
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function useFilterParams<T extends Record<string, any>>(shcemes: ShcemeConfig<T>) {
|
|
30
|
+
// 设置默认值
|
|
31
|
+
Object.values(shcemes).forEach((s) => {
|
|
32
|
+
s.resetPage ??= true;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// 好像不需要 resetPage,因为参数一旦变化肯定要重置分页的
|
|
36
|
+
(shcemes as any).page ??= { value: 1, responsive: true, resetPage: false };
|
|
37
|
+
(shcemes as any).page_size ??= { value: 10, responsive: true, resetPage: true };
|
|
38
|
+
|
|
39
|
+
const modifiableParams = reactive(mapValues(shcemes, o => toRef(o.value))) as T;
|
|
40
|
+
const pesponsiveParams = Object.keys(modifiableParams).filter(k => !!shcemes[k]?.responsive).map(k => toRef(modifiableParams, k));
|
|
41
|
+
const readonlyParams = reactive(cloneDeep(modifiableParams));
|
|
42
|
+
|
|
43
|
+
const update = (force?: boolean) => {
|
|
44
|
+
const prevParams = Object.freeze({ ...modifiableParams });
|
|
45
|
+
const beforeParams = {};
|
|
46
|
+
const modifiedParamKeys = Object.keys(readonlyParams).filter(pk => !eq(modifiableParams[pk], readonlyParams[pk]));
|
|
47
|
+
const beforeFuns = Object.entries(shcemes).filter(([k, s]) => modifiedParamKeys.includes(k) && !!s.effect).map(([_, s]) => s.effect!);
|
|
48
|
+
const needResetPage = modifiedParamKeys.some(k => !!shcemes[k]?.resetPage);
|
|
49
|
+
const pageParams = needResetPage || !!force ? { page: 1 } : {};
|
|
50
|
+
|
|
51
|
+
beforeFuns.forEach(fun => fun(beforeParams, prevParams));
|
|
52
|
+
Object.assign(readonlyParams, prevParams, pageParams, beforeParams);
|
|
53
|
+
|
|
54
|
+
for (const k in readonlyParams) {
|
|
55
|
+
// 为了拦截“只读属性赋值”的报错,因为无法判断哪些属性是只读的
|
|
56
|
+
try {
|
|
57
|
+
// @ts-expect-error 类型错误,暂时不理会
|
|
58
|
+
modifiableParams[k] = readonlyParams[k];
|
|
59
|
+
}
|
|
60
|
+
catch {}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (force)
|
|
64
|
+
Reflect.set(readonlyParams, '__t', Date.now());
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
watch(pesponsiveParams, () => update(), { deep: true });
|
|
68
|
+
|
|
69
|
+
const extra = {
|
|
70
|
+
/** 给查询器使用 */
|
|
71
|
+
$params: readonlyParams,
|
|
72
|
+
/** 更新值 */
|
|
73
|
+
update,
|
|
25
74
|
};
|
|
75
|
+
Object.setPrototypeOf(modifiableParams, extra);
|
|
26
76
|
|
|
27
|
-
return
|
|
77
|
+
return modifiableParams as typeof modifiableParams & typeof extra;
|
|
28
78
|
}
|