@peng_kai/kit 0.1.16 → 0.2.0-beta.0

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.
Files changed (67) hide show
  1. package/admin/adminPlugin.ts +47 -0
  2. package/admin/components/filter/src/FilterParam.vue +76 -78
  3. package/admin/components/filter/src/FilterReset.vue +2 -2
  4. package/admin/components/filter/src/useFilterParams.ts +75 -25
  5. package/admin/components/filter/src/useFilterQuery.ts +32 -16
  6. package/admin/components/rich-text/index.ts +2 -0
  7. package/admin/components/rich-text/src/RichText.vue +342 -0
  8. package/admin/components/rich-text/src/imageUploader.ts +34 -0
  9. package/admin/components/rich-text/src/type.d.ts +7 -0
  10. package/admin/components/text/src/Datetime.vue +47 -48
  11. package/admin/components/upload/index.ts +2 -0
  12. package/admin/components/upload/src/PictureCardUpload.vue +143 -0
  13. package/admin/components/upload/src/customRequests.ts +31 -0
  14. package/admin/defines/index.ts +1 -1
  15. package/admin/defines/route/helpers.ts +0 -1
  16. package/admin/defines/startup/defineStartup.ts +8 -1
  17. package/admin/defines/startup/index.ts +1 -1
  18. package/admin/defines/startup/{getStartups.ts → runStartup.ts} +16 -7
  19. package/admin/layout/large/Breadcrumb.vue +68 -69
  20. package/admin/layout/large/Content.vue +23 -24
  21. package/admin/layout/large/Menu.vue +68 -69
  22. package/admin/layout/large/PageTab.vue +70 -71
  23. package/admin/permission/index.ts +2 -4
  24. package/admin/permission/routerGuard.ts +41 -43
  25. package/admin/permission/vuePlugin.ts +46 -30
  26. package/admin/route-guards/collapseMenu.ts +3 -3
  27. package/admin/route-guards/pageTitle.ts +18 -19
  28. package/admin/scripts/deploy.ts +67 -0
  29. package/admin/{hooks/useMenu.ts → stores/createUseMenuStore.ts} +133 -128
  30. package/admin/{hooks/usePage.ts → stores/createUsePageStore.ts} +145 -141
  31. package/admin/stores/createUsePageTabStore.ts +43 -0
  32. package/admin/{permission/usePermission.ts → stores/createUsePermissionStore.ts} +57 -52
  33. package/admin/stores/index.ts +8 -0
  34. package/admin/styles/classCover.scss +8 -0
  35. package/antd/hooks/useAntdForm.helpers.ts +92 -8
  36. package/antd/hooks/useAntdForm.ts +55 -63
  37. package/antd/hooks/useAntdTable.ts +12 -0
  38. package/antd/index.ts +1 -1
  39. package/libs/a-calc.ts +1 -0
  40. package/libs/axios.ts +2 -0
  41. package/libs/bignumber.ts +2 -0
  42. package/libs/dayjs.ts +5 -0
  43. package/libs/echarts.ts +5 -0
  44. package/libs/localstorage-slim.ts +2 -0
  45. package/libs/lodash-es.ts +1 -0
  46. package/libs/pinia.ts +1 -0
  47. package/libs/vue-query.ts +1 -0
  48. package/libs/vueuse.ts +3 -0
  49. package/package.json +97 -53
  50. package/request/helpers.ts +20 -1
  51. package/request/interceptors/checkCode.ts +3 -3
  52. package/request/interceptors/returnResultType.ts +2 -2
  53. package/request/queryClient.ts +34 -21
  54. package/utils/upload/AwsS3.ts +76 -0
  55. package/utils/upload/fileHandlers.ts +27 -0
  56. package/utils/upload/index.ts +2 -0
  57. package/vite/index.d.ts +1 -0
  58. package/vite/index.mjs +27 -0
  59. package/vue/components/echarts/index.ts +1 -0
  60. package/vue/components/echarts/src/ECharts.vue +48 -0
  61. package/vue/components/index.ts +1 -0
  62. package/vue/components/infinite-query/src/InfiniteQuery.vue +2 -8
  63. package/vue/components/test/KitTest.vue +9 -0
  64. package/vue/components/test/testStore.ts +11 -0
  65. package/admin/hooks/index.ts +0 -5
  66. package/admin/hooks/usePageTab.ts +0 -35
  67. package/kitDependencies.ts +0 -43
@@ -0,0 +1,342 @@
1
+ <script lang="ts">
2
+ import { useVModel } from '@vueuse/core';
3
+ import CKEditor from '@ckeditor/ckeditor5-vue';
4
+ import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
5
+ import { Alignment } from '@ckeditor/ckeditor5-alignment';
6
+ import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
7
+ import { Bold, Code, Italic, Strikethrough, Subscript, Superscript, Underline } from '@ckeditor/ckeditor5-basic-styles';
8
+ import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
9
+ import { CodeBlock } from '@ckeditor/ckeditor5-code-block';
10
+ import { Essentials } from '@ckeditor/ckeditor5-essentials';
11
+ import { Font } from '@ckeditor/ckeditor5-font';
12
+ import { Heading } from '@ckeditor/ckeditor5-heading';
13
+ import { Highlight } from '@ckeditor/ckeditor5-highlight';
14
+ import { HorizontalLine } from '@ckeditor/ckeditor5-horizontal-line';
15
+ import { GeneralHtmlSupport } from '@ckeditor/ckeditor5-html-support';
16
+ import { HtmlEmbed } from '@ckeditor/ckeditor5-html-embed';
17
+ import { AutoImage, Image, ImageCaption, ImageInsert, ImageResize, ImageStyle, ImageToolbar, ImageUpload, PictureEditing } from '@ckeditor/ckeditor5-image';
18
+ import { Indent, IndentBlock } from '@ckeditor/ckeditor5-indent';
19
+ import { AutoLink, Link, LinkImage } from '@ckeditor/ckeditor5-link';
20
+ import { List, ListProperties, TodoList } from '@ckeditor/ckeditor5-list'; import { MediaEmbed } from '@ckeditor/ckeditor5-media-embed';
21
+ import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
22
+ import { RemoveFormat } from '@ckeditor/ckeditor5-remove-format';
23
+ import { Table, TableCaption, TableCellProperties, TableColumnResize, TableProperties, TableToolbar } from '@ckeditor/ckeditor5-table';
24
+ import { TextTransformation } from '@ckeditor/ckeditor5-typing';
25
+ import { WordCount } from '@ckeditor/ckeditor5-word-count';
26
+ import { SourceEditing } from '@ckeditor/ckeditor5-source-editing';
27
+ import { ShowBlocks } from '@ckeditor/ckeditor5-show-blocks';
28
+ import type { CKEditorConfig, PluginConstructor } from './type';
29
+
30
+ import '@ckeditor/ckeditor5-build-classic/build/translations/zh-cn';
31
+ import '@ckeditor/ckeditor5-html-embed/build/translations/zh-cn';
32
+
33
+ const editorConfig: CKEditorConfig = {
34
+ plugins: [
35
+ Alignment,
36
+ Autoformat,
37
+ AutoImage,
38
+ AutoLink,
39
+ BlockQuote,
40
+ Bold,
41
+ Code,
42
+ CodeBlock,
43
+ Essentials,
44
+ Font,
45
+ GeneralHtmlSupport,
46
+ HtmlEmbed,
47
+ Heading,
48
+ Highlight,
49
+ HorizontalLine,
50
+ Image,
51
+ ImageCaption,
52
+ ImageInsert,
53
+ ImageResize,
54
+ ImageStyle,
55
+ ImageToolbar,
56
+ ImageUpload,
57
+ Indent,
58
+ IndentBlock,
59
+ Italic,
60
+ Link,
61
+ LinkImage,
62
+ List,
63
+ ListProperties,
64
+ TodoList,
65
+ MediaEmbed,
66
+ Paragraph,
67
+ PictureEditing,
68
+ RemoveFormat,
69
+ Strikethrough,
70
+ Subscript,
71
+ Superscript,
72
+ Table,
73
+ TableCaption,
74
+ TableCellProperties,
75
+ TableColumnResize,
76
+ TableProperties,
77
+ TableToolbar,
78
+ TextTransformation,
79
+ Underline,
80
+ WordCount,
81
+ SourceEditing,
82
+ ShowBlocks,
83
+ ],
84
+ toolbar: {
85
+ items: [
86
+ 'undo',
87
+ 'redo',
88
+ '|',
89
+ 'showBlocks',
90
+ 'selectAll',
91
+ '|',
92
+ 'heading',
93
+ '|',
94
+ 'fontSize',
95
+ 'fontFamily',
96
+ 'fontColor',
97
+ 'fontBackgroundColor',
98
+ '|',
99
+ 'bold',
100
+ 'italic',
101
+ 'underline',
102
+ {
103
+ label: 'Formatting',
104
+ icon: 'text',
105
+ items: ['strikethrough', 'subscript', 'superscript', 'code', 'horizontalLine', '|', 'removeFormat'],
106
+ },
107
+ '|',
108
+ 'link',
109
+ 'insertImage',
110
+ 'insertTable',
111
+ {
112
+ label: 'Insert',
113
+ icon: 'plus',
114
+ items: ['highlight', 'blockQuote', 'mediaEmbed', 'codeBlock', 'htmlEmbed'],
115
+ },
116
+ '|',
117
+ 'alignment',
118
+ '|',
119
+ 'bulletedList',
120
+ 'numberedList',
121
+ 'todoList',
122
+ 'outdent',
123
+ 'indent',
124
+ '|',
125
+ 'sourceEditing',
126
+ ],
127
+ shouldNotGroupWhenFull: true,
128
+ },
129
+ htmlSupport: {
130
+ allow: [
131
+ {
132
+ name: /^.*$/,
133
+ styles: true,
134
+ attributes: true,
135
+ classes: true,
136
+ },
137
+ ],
138
+ },
139
+ fontSize: {
140
+ options: [10, 12, 14, 'default', 18, 20, 22],
141
+ supportAllValues: true,
142
+ },
143
+ image: {
144
+ styles: [
145
+ 'alignCenter',
146
+ 'alignLeft',
147
+ 'alignRight',
148
+ ] as any,
149
+ resizeOptions: [
150
+ { name: 'resizeImage:original', label: 'Original', value: null },
151
+ { name: 'resizeImage:25', label: '25%', value: '25' },
152
+ { name: 'resizeImage:50', label: '50%', value: '50' },
153
+ { name: 'resizeImage:75', label: '75%', value: '75' },
154
+ ],
155
+ toolbar: [
156
+ 'imageTextAlternative',
157
+ 'toggleImageCaption',
158
+ '|',
159
+ 'imageStyle:inline',
160
+ 'imageStyle:wrapText',
161
+ 'imageStyle:breakText',
162
+ 'imageStyle:side',
163
+ '|',
164
+ 'resizeImage',
165
+ ],
166
+ resizeUnit: 'px',
167
+ },
168
+ list: {
169
+ properties: {
170
+ styles: true,
171
+ startIndex: true,
172
+ reversed: true,
173
+ },
174
+ },
175
+ link: {
176
+ addTargetToExternalLinks: true,
177
+ defaultProtocol: 'https://',
178
+ decorators: {
179
+ toggleDownloadable: {
180
+ mode: 'manual',
181
+ label: 'Downloadable',
182
+ attributes: {
183
+ download: 'file',
184
+ },
185
+ },
186
+ },
187
+ },
188
+ table: {
189
+ contentToolbar: [
190
+ 'tableColumn',
191
+ 'tableRow',
192
+ 'mergeTableCells',
193
+ 'tableProperties',
194
+ 'tableCellProperties',
195
+ 'toggleTableCaption',
196
+ ],
197
+ },
198
+ htmlEmbed: {
199
+ showPreviews: true,
200
+ },
201
+ wordCount: {
202
+ displayWords: true,
203
+ },
204
+ };
205
+ </script>
206
+
207
+ <script setup lang="ts">
208
+ const props = withDefaults(defineProps<{
209
+ modelValue?: string
210
+ plugins?: PluginConstructor[]
211
+ }>(), {
212
+ modelValue: '',
213
+ plugins: () => [],
214
+ });
215
+ const emits = defineEmits<{
216
+ (e: 'update:modelValue', value: string): void
217
+ }>();
218
+
219
+ const editorData = useVModel(props, 'modelValue', emits);
220
+ const localeEditorConfig = { ...editorConfig };
221
+ localeEditorConfig.extraPlugins = [...props.plugins];
222
+ </script>
223
+
224
+ <template>
225
+ <div class="editor-wrapper">
226
+ <CKEditor.component v-model="editorData" :editor="ClassicEditor" :config="localeEditorConfig" />
227
+ </div>
228
+ </template>
229
+
230
+ <style scoped lang="scss">
231
+ .editor-wrapper {
232
+ :deep(.ck-editor__main > .ck-content) {
233
+ height: 500px;
234
+ overflow: auto;
235
+ }
236
+ }
237
+
238
+ :root.dark .editor-wrapper {
239
+ /* Overrides the border radius setting in the theme. */
240
+ // --ck-border-radius: 4px;
241
+
242
+ /* Overrides the default font size in the theme. */
243
+ // --ck-font-size-base: 14px;
244
+
245
+ /* Helper variables to avoid duplication in the colors. */
246
+ --ck-custom-background: hsl(270deg 1% 29%);
247
+ --ck-custom-foreground: hsl(255deg 3% 18%);
248
+ --ck-custom-border: hsl(300deg 1% 22%);
249
+ --ck-custom-white: hsl(0deg 0% 100%);
250
+
251
+ /* -- Overrides generic colors. ------------------------------------------------------------- */
252
+
253
+ --ck-color-base-foreground: var(--ck-custom-background);
254
+ --ck-color-focus-border: hsl(208deg 90% 62%);
255
+ --ck-color-text: hsl(0deg 0% 98%);
256
+ --ck-color-shadow-drop: hsl(0deg 0% 0% / 20%);
257
+ --ck-color-shadow-inner: hsl(0deg 0% 0% / 10%);
258
+
259
+ /* -- Overrides the default .ck-button class colors. ---------------------------------------- */
260
+
261
+ --ck-color-button-default-background: var(--ck-custom-background);
262
+ --ck-color-button-default-hover-background: hsl(270deg 1% 22%);
263
+ --ck-color-button-default-active-background: hsl(270deg 2% 20%);
264
+ --ck-color-button-default-active-shadow: hsl(270deg 2% 23%);
265
+ --ck-color-button-default-disabled-background: var(--ck-custom-background);
266
+ --ck-color-button-on-background: var(--ck-custom-foreground);
267
+ --ck-color-button-on-hover-background: hsl(255deg 4% 16%);
268
+ --ck-color-button-on-active-background: hsl(255deg 4% 14%);
269
+ --ck-color-button-on-active-shadow: hsl(240deg 3% 19%);
270
+ --ck-color-button-on-disabled-background: var(--ck-custom-foreground);
271
+ --ck-color-button-action-background: hsl(168deg 76% 42%);
272
+ --ck-color-button-action-hover-background: hsl(168deg 76% 38%);
273
+ --ck-color-button-action-active-background: hsl(168deg 76% 36%);
274
+ --ck-color-button-action-active-shadow: hsl(168deg 75% 34%);
275
+ --ck-color-button-action-disabled-background: hsl(168deg 76% 42%);
276
+ --ck-color-button-action-text: var(--ck-custom-white);
277
+ --ck-color-button-save: hsl(120deg 100% 46%);
278
+ --ck-color-button-cancel: hsl(15deg 100% 56%);
279
+
280
+ /* -- Overrides the default .ck-dropdown class colors. -------------------------------------- */
281
+
282
+ --ck-color-dropdown-panel-background: var(--ck-custom-background);
283
+ --ck-color-dropdown-panel-border: var(--ck-custom-foreground);
284
+
285
+ /* -- Overrides the default .ck-splitbutton class colors. ----------------------------------- */
286
+
287
+ --ck-color-split-button-hover-background: var(--ck-color-button-default-hover-background);
288
+ --ck-color-split-button-hover-border: var(--ck-custom-foreground);
289
+
290
+ /* -- Overrides the default .ck-input class colors. ----------------------------------------- */
291
+
292
+ --ck-color-input-background: var(--ck-custom-background);
293
+ --ck-color-input-border: hsl(257deg 3% 43%);
294
+ --ck-color-input-text: hsl(0deg 0% 98%);
295
+ --ck-color-input-disabled-background: hsl(255deg 4% 21%);
296
+ --ck-color-input-disabled-border: hsl(250deg 3% 38%);
297
+ --ck-color-input-disabled-text: hsl(0deg 0% 78%);
298
+
299
+ /* -- Overrides the default .ck-labeled-field-view class colors. ---------------------------- */
300
+
301
+ --ck-color-labeled-field-label-background: var(--ck-custom-background);
302
+
303
+ /* -- Overrides the default .ck-list class colors. ------------------------------------------ */
304
+
305
+ --ck-color-list-background: var(--ck-custom-background);
306
+ --ck-color-list-button-hover-background: var(--ck-custom-foreground);
307
+ --ck-color-list-button-on-background: hsl(208deg 88% 52%);
308
+ --ck-color-list-button-on-text: var(--ck-custom-white);
309
+
310
+ /* -- Overrides the default .ck-balloon-panel class colors. --------------------------------- */
311
+
312
+ --ck-color-panel-background: var(--ck-custom-background);
313
+ --ck-color-panel-border: var(--ck-custom-border);
314
+
315
+ /* -- Overrides the default .ck-toolbar class colors. --------------------------------------- */
316
+
317
+ --ck-color-toolbar-background: var(--ck-custom-background);
318
+ --ck-color-toolbar-border: var(--ck-custom-border);
319
+
320
+ /* -- Overrides the default .ck-tooltip class colors. --------------------------------------- */
321
+
322
+ --ck-color-tooltip-background: hsl(252deg 7% 14%);
323
+ --ck-color-tooltip-text: hsl(0deg 0% 93%);
324
+
325
+ /* -- Overrides the default colors used by the ckeditor5-image package. --------------------- */
326
+
327
+ --ck-color-image-caption-background: hsl(0deg 0% 97%);
328
+ --ck-color-image-caption-text: hsl(0deg 0% 20%);
329
+
330
+ /* -- Overrides the default colors used by the ckeditor5-widget package. -------------------- */
331
+
332
+ --ck-color-widget-blurred-border: hsl(0deg 0% 87%);
333
+ --ck-color-widget-hover-border: hsl(43deg 100% 68%);
334
+ --ck-color-widget-editable-focus-background: var(--ck-custom-white);
335
+
336
+ /* -- Overrides the default colors used by the ckeditor5-link package. ---------------------- */
337
+
338
+ --ck-color-link-default: hsl(190deg 100% 75%);
339
+ --ck-color-base-background: #141414;
340
+ --ck-color-base-border: #424242;
341
+ }
342
+ </style>
@@ -0,0 +1,34 @@
1
+ import type { AwsS3 } from '../../../../utils/upload';
2
+ import type { PluginConstructor } from './type';
3
+
4
+ export { createImageUploaderForAws3 };
5
+
6
+ function createImageUploaderForAws3(awsS3: AwsS3, rootDir?: string): PluginConstructor {
7
+ return function (editor) {
8
+ editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
9
+ return {
10
+ async upload() {
11
+ const file = await loader.file;
12
+
13
+ if (file) {
14
+ const { uploader, fileURL } = await awsS3.upload(file, rootDir);
15
+
16
+ uploader.on('httpUploadProgress', (progress) => {
17
+ loader.uploadTotal = progress.total ?? null;
18
+ loader.uploaded = progress.loaded ?? 0;
19
+ });
20
+
21
+ await uploader.done();
22
+
23
+ return { default: fileURL };
24
+ }
25
+
26
+ return {};
27
+ },
28
+ abort() {
29
+
30
+ },
31
+ };
32
+ };
33
+ };
34
+ }
@@ -0,0 +1,7 @@
1
+ import type CKEditor from '@ckeditor/ckeditor5-vue';
2
+ import type { RequiredDeep } from 'type-fest';
3
+ import type { ComponentProps } from 'vue-component-type-helpers';
4
+
5
+ export type CKEditorProps = NonNullable<ComponentProps<typeof CKEditor['component']>>;
6
+ export type CKEditorConfig = NonNullable<CKEditorProps['config']>;
7
+ export type PluginConstructor = NonNullable<CKEditorConfig['extraPlugins']>[number];
@@ -1,48 +1,47 @@
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" class="text">{{ timestamp ? dayjs(timestamp).format(props.template) : '-' }}</span>
41
- </ATooltip>
42
- </template>
43
-
44
- <style scoped lang="scss">
45
- .text {
46
- font-variant-numeric: tabular-nums;
47
- }
48
- </style>
1
+ <script setup lang="ts">
2
+ import { Tooltip as ATooltip } from 'ant-design-vue';
3
+ import { computed } from 'vue';
4
+ import { dayjs } from '../../../../utils/date';
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 timestamp = computed(() => {
20
+ let tsStr = String(props.timestamp);
21
+
22
+ if (tsStr.length === 10)
23
+ tsStr += '000';
24
+ if (tsStr.length !== 13)
25
+ return;
26
+
27
+ const tsNum = Number.parseInt(tsStr);
28
+
29
+ return Number.isNaN(tsNum) ? undefined : tsNum;
30
+ });
31
+ </script>
32
+
33
+ <template>
34
+ <ATooltip destroyTooltipOnHide>
35
+ <template v-if="timestamp" #title>
36
+ <div>{{ dayjs(timestamp).fromNow?.() }}</div>
37
+ <div>{{ dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss') }}</div>
38
+ </template>
39
+ <span v-bind="$attrs" class="text">{{ timestamp ? dayjs(timestamp).format(props.template) : '-' }}</span>
40
+ </ATooltip>
41
+ </template>
42
+
43
+ <style scoped lang="scss">
44
+ .text {
45
+ font-variant-numeric: tabular-nums;
46
+ }
47
+ </style>
@@ -0,0 +1,2 @@
1
+ export { default as PictureCardUpload } from './src/PictureCardUpload.vue';
2
+ export { createAwsS3Request } from './src/customRequests';
@@ -0,0 +1,143 @@
1
+ <script lang="ts">
2
+ import { message } from 'ant-design-vue';
3
+ import type { UploadProps } from 'ant-design-vue';
4
+ import type { PreviewGroupPreview } from 'ant-design-vue/es/vc-image/src/PreviewGroup';
5
+ import { useVModel } from '@vueuse/core';
6
+ import { ref } from 'vue';
7
+ import type { AwsS3 } from '../../../../utils/upload';
8
+ import { createAwsS3Request } from './customRequests';
9
+
10
+ type UploadFile = Parameters<NonNullable<UploadProps['onPreview']>>['0'];
11
+ </script>
12
+
13
+ <script lang="ts" setup>
14
+ const props = withDefaults(
15
+ defineProps<{
16
+ /** 上传文件的数组 */
17
+ modelValue?: UploadFile[]
18
+ /** awsS3 实例对象 */
19
+ awsS3: AwsS3
20
+ /** 根目录 */
21
+ rootDir?: string
22
+ /** 可以上传的最大文件数 */
23
+ maxCount?: number
24
+ /** 显示图片卡的行数,或 “auto-fill” 以根据可用空间自动调整行数。 */
25
+ rowCount?: number | 'auto-fill'
26
+ /** 图片卡的大小 */
27
+ cardSize?: string
28
+ /** 允许的图片后缀 */
29
+ allowExts?: string[]
30
+ }>(),
31
+ {
32
+ modelValue: () => [],
33
+ maxCount: 2,
34
+ rowCount: 'auto-fill',
35
+ cardSize: '90px',
36
+ allowExts: () => ['jpg', 'jpeg', 'png', 'gif', 'apng', 'webp', 'svg'],
37
+ },
38
+ );
39
+ const emits = defineEmits<{
40
+ (e: 'update:modelValue', value: UploadFile[]): void
41
+ }>();
42
+
43
+ const fileList = useVModel(props, 'modelValue', emits);
44
+ const preview = ref<PreviewGroupPreview>({
45
+ visible: false,
46
+ onVisibleChange(value) {
47
+ preview.value.visible = value;
48
+ },
49
+ });
50
+
51
+ function handlePreview(file: UploadFile) {
52
+ preview.value.src = file.url || file.thumbUrl;
53
+ preview.value.visible = true;
54
+ preview.value.current = fileList.value?.findIndex(item => preview.value.src === (item.url || item.thumbUrl)) ?? 0;
55
+ }
56
+
57
+ function beforeUpload(file: UploadFile) {
58
+ const isImg = props.allowExts.some(item => String(file.type).toLowerCase().includes(item));
59
+
60
+ if (!isImg)
61
+ message.error(`${file.name} 不是图片 (${props.allowExts.join(',')})`);
62
+
63
+ return isImg;
64
+ }
65
+ </script>
66
+
67
+ <template>
68
+ <div :class="{ 'w-fit': typeof props.rowCount === 'number' }">
69
+ <AUpload
70
+ v-model:fileList="fileList" class="pic-card-upload" listType="picture-card" multiple :maxCount="$props.maxCount"
71
+ :beforeUpload="beforeUpload" :customRequest="createAwsS3Request($props.awsS3, $props.rootDir)" @preview="handlePreview"
72
+ >
73
+ <i v-if="fileList.length < props.maxCount" class="i-ant-design:plus-outlined text-26px text-$antd-colorTextTertiary" />
74
+ </AUpload>
75
+
76
+ <div style="display: none">
77
+ <AImagePreviewGroup :preview="preview">
78
+ <AImage
79
+ v-for="item of fileList"
80
+ :key="item.uid"
81
+ :src="item.url || item.thumbUrl"
82
+ />
83
+ </AImagePreviewGroup>
84
+ </div>
85
+ </div>
86
+ </template>
87
+
88
+ <style scoped lang="scss">
89
+ /* stylelint-disable function-no-unknown */
90
+
91
+ .pic-card-upload.ant-upload-wrapper {
92
+ display: block;
93
+
94
+ :deep(.ant-upload-list.ant-upload-list-picture-card .ant-upload-list-item) {
95
+ padding: 0;
96
+ overflow: hidden;
97
+ }
98
+
99
+ :deep(.ant-upload-list::before) {
100
+ display: none;
101
+ }
102
+
103
+ :deep(.ant-upload-list-picture-card) {
104
+ display: grid;
105
+ grid-template-columns: repeat(v-bind('$props.rowCount'), v-bind('$props.cardSize'));
106
+ gap: 10px;
107
+ }
108
+
109
+ :deep(.ant-upload-list.ant-upload-list-picture-card .ant-upload-list-item-container),
110
+ :deep(.ant-upload.ant-upload-select) {
111
+ width: v-bind('$props.cardSize');
112
+ height: v-bind('$props.cardSize');
113
+ margin: 0;
114
+ }
115
+
116
+ :deep(.ant-upload-list.ant-upload-list-picture-card .ant-upload-list-item::before ) {
117
+ width: 100%;
118
+ height: 100%;
119
+ }
120
+
121
+ :deep(.ant-upload-list.ant-upload-list-picture-card .ant-upload-list-item-thumbnail img) {
122
+ object-fit: cover;
123
+ }
124
+
125
+ :deep(.ant-upload-list.ant-upload-list-picture-card .ant-upload-list-item-progress) {
126
+ bottom: calc(50% - 22px);
127
+ left: 50%;
128
+ line-height: 1em;
129
+ transform: translateX(-50%);
130
+ }
131
+
132
+ :deep(.ant-upload-list.ant-upload-list-picture-card .ant-upload-list-item-thumbnail) {
133
+ display: flex;
134
+ align-items: center;
135
+ justify-content: center;
136
+ }
137
+
138
+ :deep(.ant-upload-list.ant-upload-list-picture-card .ant-upload-list-item-file+.ant-upload-list-item-name) {
139
+ left: 50%;
140
+ transform: translateX(-50%);
141
+ }
142
+ }
143
+ </style>
@@ -0,0 +1,31 @@
1
+ import type { UploadProps } from 'ant-design-vue';
2
+ import type { AwsS3 } from '../../../../utils/upload';
3
+
4
+ type UploadRequestOption = Parameters<NonNullable<UploadProps['customRequest']>>['0'];
5
+
6
+ export function createAwsS3Request(awsS3: AwsS3, rootPath?: string) {
7
+ return async (options: UploadRequestOption) => {
8
+ if (!(options.file instanceof File))
9
+ throw new Error('options.file 不是 File 对象');
10
+
11
+ const { uploader, fileURL } = await awsS3.upload(options.file, rootPath);
12
+
13
+ uploader.on('httpUploadProgress', (progress) => {
14
+ const percent = (progress.loaded ?? 0) / (progress.total ?? 1) * 100;
15
+ options.onProgress?.({ percent });
16
+ });
17
+
18
+ await uploader.done();
19
+ options.onSuccess?.({
20
+ uid: (options.file as any).uid,
21
+ name: fileURL.split('/').pop(),
22
+ url: fileURL,
23
+ });
24
+
25
+ return {
26
+ abort() {
27
+ uploader.abort();
28
+ },
29
+ };
30
+ };
31
+ }
@@ -1,4 +1,4 @@
1
1
  export { definePage } from './page';
2
2
  export { defineRoute, getRoutes } from './route';
3
3
  export { defineRouteGuard, getRouteGuards } from './route-guard';
4
- export { defineStartup, getStartups } from './startup';
4
+ export { defineStartup, runStartup } from './startup';
@@ -38,7 +38,6 @@ export function getMenusByRouter(router: Router) {
38
38
 
39
39
  export function printRounesNameInterface(routes: { name: PropertyKey }[]) {
40
40
  console.groupCollapsed('路由命名');
41
- console.log('复制以下内容到 AppRouteNames');
42
41
  const routesName = new Set();
43
42
  routes.forEach((route) => {
44
43
  if (typeof route.name === 'string')
@@ -1,6 +1,13 @@
1
1
  import type { App } from 'vue';
2
2
 
3
- export type StartupFn = (app: App) => void | Promise<void>;
3
+ export interface IHooks {
4
+ onMount: (cb: () => void) => void
5
+ }
6
+
7
+ export type StartupFn = (
8
+ app: App,
9
+ hooks: IHooks
10
+ ) => (void | Promise<void>);
4
11
 
5
12
  export const StartupSymbol = Symbol('app-startup');
6
13