Commit 661db0c767772bb7a30da9d3eeaf2b47858ccf0b
1 parent
a161bfa8
perf(upload): improve upload component
Showing
38 changed files
with
433 additions
and
185 deletions
CHANGELOG.zh_CN.md
README.en-US.md
@@ -226,10 +226,10 @@ yarn clean:lib # Delete node_modules, supported window | @@ -226,10 +226,10 @@ yarn clean:lib # Delete node_modules, supported window | ||
226 | - [x] Data import and export | 226 | - [x] Data import and export |
227 | - [x] Global error handling | 227 | - [x] Global error handling |
228 | - [x] Rich text component | 228 | - [x] Rich text component |
229 | +- [x] Upload component | ||
229 | 230 | ||
230 | ## Developing features | 231 | ## Developing features |
231 | 232 | ||
232 | -- [ ] Upload component | ||
233 | - [ ] Theme configuration | 233 | - [ ] Theme configuration |
234 | - [ ] Dark theme | 234 | - [ ] Dark theme |
235 | - [ ] Build CDN | 235 | - [ ] Build CDN |
README.md
@@ -228,10 +228,10 @@ yarn clean:lib # 删除node_modules,兼容window系统 | @@ -228,10 +228,10 @@ yarn clean:lib # 删除node_modules,兼容window系统 | ||
228 | - [x] 系统性能优化 | 228 | - [x] 系统性能优化 |
229 | - [x] 全局错误处理 | 229 | - [x] 全局错误处理 |
230 | - [x] 富文本组件 | 230 | - [x] 富文本组件 |
231 | +- [x] 上传组件 | ||
231 | 232 | ||
232 | ## 正在开发的功能 | 233 | ## 正在开发的功能 |
233 | 234 | ||
234 | -- [ ] 上传组件 | ||
235 | - [ ] 主题配置 | 235 | - [ ] 主题配置 |
236 | - [ ] 黑暗主题 | 236 | - [ ] 黑暗主题 |
237 | - [ ] 打包 CDN | 237 | - [ ] 打包 CDN |
package.json
@@ -22,8 +22,8 @@ | @@ -22,8 +22,8 @@ | ||
22 | }, | 22 | }, |
23 | "dependencies": { | 23 | "dependencies": { |
24 | "@iconify/iconify": "^2.0.0-rc.2", | 24 | "@iconify/iconify": "^2.0.0-rc.2", |
25 | - "@vueuse/core": "^4.0.0-beta.40", | ||
26 | - "ant-design-vue": "^2.0.0-beta.15", | 25 | + "@vueuse/core": "^4.0.0-beta.41", |
26 | + "ant-design-vue": "^2.0.0-rc.1", | ||
27 | "apexcharts": "3.22.0", | 27 | "apexcharts": "3.22.0", |
28 | "axios": "^0.21.0", | 28 | "axios": "^0.21.0", |
29 | "echarts": "^4.9.0", | 29 | "echarts": "^4.9.0", |
@@ -33,10 +33,10 @@ | @@ -33,10 +33,10 @@ | ||
33 | "nprogress": "^0.2.0", | 33 | "nprogress": "^0.2.0", |
34 | "path-to-regexp": "^6.2.0", | 34 | "path-to-regexp": "^6.2.0", |
35 | "qrcode": "^1.4.4", | 35 | "qrcode": "^1.4.4", |
36 | - "vditor": "^3.6.0", | 36 | + "vditor": "^3.6.2", |
37 | "vue": "^3.0.2", | 37 | "vue": "^3.0.2", |
38 | "vue-i18n": "^9.0.0-beta.6", | 38 | "vue-i18n": "^9.0.0-beta.6", |
39 | - "vue-router": "^4.0.0-rc.2", | 39 | + "vue-router": "^4.0.0-rc.3", |
40 | "vuex": "^4.0.0-rc.1", | 40 | "vuex": "^4.0.0-rc.1", |
41 | "vuex-module-decorators": "^1.0.1", | 41 | "vuex-module-decorators": "^1.0.1", |
42 | "xlsx": "^0.16.8", | 42 | "xlsx": "^0.16.8", |
@@ -45,11 +45,11 @@ | @@ -45,11 +45,11 @@ | ||
45 | "devDependencies": { | 45 | "devDependencies": { |
46 | "@commitlint/cli": "^11.0.0", | 46 | "@commitlint/cli": "^11.0.0", |
47 | "@commitlint/config-conventional": "^11.0.0", | 47 | "@commitlint/config-conventional": "^11.0.0", |
48 | - "@iconify/json": "^1.1.254", | 48 | + "@iconify/json": "^1.1.258", |
49 | "@ls-lint/ls-lint": "^1.9.2", | 49 | "@ls-lint/ls-lint": "^1.9.2", |
50 | "@purge-icons/generated": "^0.4.1", | 50 | "@purge-icons/generated": "^0.4.1", |
51 | "@types/echarts": "^4.9.0", | 51 | "@types/echarts": "^4.9.0", |
52 | - "@types/fs-extra": "^9.0.2", | 52 | + "@types/fs-extra": "^9.0.4", |
53 | "@types/koa-static": "^4.0.1", | 53 | "@types/koa-static": "^4.0.1", |
54 | "@types/lodash-es": "^4.17.3", | 54 | "@types/lodash-es": "^4.17.3", |
55 | "@types/mockjs": "^1.0.3", | 55 | "@types/mockjs": "^1.0.3", |
src/api/demo/model/uploadModel.ts renamed to src/api/sys/model/uploadModel.ts
src/api/demo/upload.ts renamed to src/api/sys/upload.ts
src/components/Drawer/src/props.ts
@@ -24,7 +24,7 @@ export const footerProps = { | @@ -24,7 +24,7 @@ export const footerProps = { | ||
24 | okButtonProps: Object as PropType<any>, | 24 | okButtonProps: Object as PropType<any>, |
25 | okText: { | 25 | okText: { |
26 | type: String as PropType<string>, | 26 | type: String as PropType<string>, |
27 | - default: '保存', | 27 | + default: '确认', |
28 | }, | 28 | }, |
29 | okType: { | 29 | okType: { |
30 | type: String as PropType<string>, | 30 | type: String as PropType<string>, |
src/components/Form/src/BasicForm.vue
@@ -44,7 +44,6 @@ | @@ -44,7 +44,6 @@ | ||
44 | import { useFormValues } from './hooks/useFormValues'; | 44 | import { useFormValues } from './hooks/useFormValues'; |
45 | import useAdvanced from './hooks/useAdvanced'; | 45 | import useAdvanced from './hooks/useAdvanced'; |
46 | import { useFormAction } from './hooks/useFormAction'; | 46 | import { useFormAction } from './hooks/useFormAction'; |
47 | - | ||
48 | export default defineComponent({ | 47 | export default defineComponent({ |
49 | name: 'BasicForm', | 48 | name: 'BasicForm', |
50 | components: { FormItem, Form, Row, FormAction }, | 49 | components: { FormItem, Form, Row, FormAction }, |
src/components/Icon/index.tsx
@@ -18,7 +18,7 @@ export default defineComponent({ | @@ -18,7 +18,7 @@ export default defineComponent({ | ||
18 | // icon size | 18 | // icon size |
19 | size: { | 19 | size: { |
20 | type: [String, Number] as PropType<string | number>, | 20 | type: [String, Number] as PropType<string | number>, |
21 | - default: 14, | 21 | + default: 16, |
22 | }, | 22 | }, |
23 | prefix: { | 23 | prefix: { |
24 | type: String as PropType<string>, | 24 | type: String as PropType<string>, |
src/components/Modal/src/props.ts
1 | import type { PropType } from 'vue'; | 1 | import type { PropType } from 'vue'; |
2 | +import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes'; | ||
2 | export const modalProps = { | 3 | export const modalProps = { |
3 | visible: Boolean as PropType<boolean>, | 4 | visible: Boolean as PropType<boolean>, |
4 | // open drag | 5 | // open drag |
@@ -16,7 +17,7 @@ export const modalProps = { | @@ -16,7 +17,7 @@ export const modalProps = { | ||
16 | }, | 17 | }, |
17 | okText: { | 18 | okText: { |
18 | type: String as PropType<string>, | 19 | type: String as PropType<string>, |
19 | - default: '保存', | 20 | + default: '确认', |
20 | }, | 21 | }, |
21 | closeFunc: Function as PropType<() => Promise<boolean>>, | 22 | closeFunc: Function as PropType<() => Promise<boolean>>, |
22 | }; | 23 | }; |
@@ -100,9 +101,9 @@ export const basicProps = Object.assign({}, modalProps, { | @@ -100,9 +101,9 @@ export const basicProps = Object.assign({}, modalProps, { | ||
100 | default: 'primary', | 101 | default: 'primary', |
101 | }, | 102 | }, |
102 | 103 | ||
103 | - okButtonProps: Object as PropType<any>, | 104 | + okButtonProps: Object as PropType<ButtonProps>, |
104 | 105 | ||
105 | - cancelButtonProps: Object as PropType<any>, | 106 | + cancelButtonProps: Object as PropType<ButtonProps>, |
106 | 107 | ||
107 | title: { | 108 | title: { |
108 | type: String as PropType<string>, | 109 | type: String as PropType<string>, |
src/components/Qrcode/src/index.vue
@@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
7 | import { defineComponent, watchEffect, PropType, ref, unref } from 'vue'; | 7 | import { defineComponent, watchEffect, PropType, ref, unref } from 'vue'; |
8 | import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus'; | 8 | import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus'; |
9 | import { toDataURL } from 'qrcode'; | 9 | import { toDataURL } from 'qrcode'; |
10 | - import { downloadByUrl } from '/@/utils/file/FileDownload'; | 10 | + import { downloadByUrl } from '/@/utils/file/download'; |
11 | 11 | ||
12 | export default defineComponent({ | 12 | export default defineComponent({ |
13 | name: 'QrCode', | 13 | name: 'QrCode', |
src/components/Table/src/BasicTable.vue
src/components/Table/src/hooks/useDataSource.ts
@@ -84,7 +84,7 @@ export function useDataSource( | @@ -84,7 +84,7 @@ export function useDataSource( | ||
84 | const { api, searchInfo, fetchSetting, beforeFetch, afterFetch, useSearchForm } = unref( | 84 | const { api, searchInfo, fetchSetting, beforeFetch, afterFetch, useSearchForm } = unref( |
85 | propsRef | 85 | propsRef |
86 | ); | 86 | ); |
87 | - if (!api && !isFunction(api)) return; | 87 | + if (!api || !isFunction(api)) return; |
88 | try { | 88 | try { |
89 | loadingRef.value = true; | 89 | loadingRef.value = true; |
90 | const { pageField, sizeField, listField, totalField } = fetchSetting || FETCH_SETTING; | 90 | const { pageField, sizeField, listField, totalField } = fetchSetting || FETCH_SETTING; |
src/components/Table/src/props.ts
@@ -16,7 +16,10 @@ export const basicProps = { | @@ -16,7 +16,10 @@ export const basicProps = { | ||
16 | tableSetting: { | 16 | tableSetting: { |
17 | type: Object as PropType<TableSetting>, | 17 | type: Object as PropType<TableSetting>, |
18 | }, | 18 | }, |
19 | - | 19 | + inset: { |
20 | + type: Boolean as PropType<boolean>, | ||
21 | + default: false, | ||
22 | + }, | ||
20 | sortFn: { | 23 | sortFn: { |
21 | type: Function as PropType<(sortInfo: SorterResult) => any>, | 24 | type: Function as PropType<(sortInfo: SorterResult) => any>, |
22 | default: DEFAULT_SORT_FN, | 25 | default: DEFAULT_SORT_FN, |
src/components/Table/src/style/index.less
src/components/Table/src/types/table.ts
@@ -126,6 +126,8 @@ export interface TableSetting { | @@ -126,6 +126,8 @@ export interface TableSetting { | ||
126 | export interface BasicTableProps<T = any> { | 126 | export interface BasicTableProps<T = any> { |
127 | // 自定义排序方法 | 127 | // 自定义排序方法 |
128 | sortFn?: (sortInfo: SorterResult) => any; | 128 | sortFn?: (sortInfo: SorterResult) => any; |
129 | + // 取消表格的默认padding | ||
130 | + inset?: boolean; | ||
129 | // 显示表格设置 | 131 | // 显示表格设置 |
130 | showTableSetting?: boolean; | 132 | showTableSetting?: boolean; |
131 | tableSetting?: TableSetting; | 133 | tableSetting?: TableSetting; |
src/components/Upload/index.ts
src/components/Upload/src/UploadContainer.vue renamed to src/components/Upload/src/BasicUpload.vue
1 | <template> | 1 | <template> |
2 | <div> | 2 | <div> |
3 | <a-button-group> | 3 | <a-button-group> |
4 | - <a-button type="primary" @click="openUploadModal">上传</a-button> | ||
5 | - <a-button @click="openPreviewModal"> | ||
6 | - <Icon icon="ant-design:eye-outlined" /> | 4 | + <a-button type="primary" @click="openUploadModal" preIcon="ant-design:cloud-upload-outlined"> |
5 | + 上传 | ||
7 | </a-button> | 6 | </a-button> |
7 | + <Tooltip placement="bottom" v-if="showPreview"> | ||
8 | + <template #title> | ||
9 | + 已上传 | ||
10 | + <template v-if="fileListRef.length">{{ fileListRef.length }}</template> | ||
11 | + </template> | ||
12 | + <a-button @click="openPreviewModal"> | ||
13 | + <Icon icon="ant-design:eye-outlined" /> | ||
14 | + <template v-if="fileListRef.length && showPreviewNumber"> | ||
15 | + {{ fileListRef.length }} | ||
16 | + </template> | ||
17 | + </a-button> | ||
18 | + </Tooltip> | ||
8 | </a-button-group> | 19 | </a-button-group> |
9 | - <UploadModal v-bind="$props" @register="registerUploadModal" @change="handleChange" /> | 20 | + |
21 | + <UploadModal v-bind="bindValue" @register="registerUploadModal" @change="handleChange" /> | ||
22 | + | ||
10 | <UploadPreviewModal | 23 | <UploadPreviewModal |
11 | :value="fileListRef" | 24 | :value="fileListRef" |
12 | @register="registerPreviewModal" | 25 | @register="registerPreviewModal" |
13 | - @change="handlePreviewChange" | 26 | + @list-change="handlePreviewChange" |
14 | /> | 27 | /> |
15 | </div> | 28 | </div> |
16 | </template> | 29 | </template> |
17 | <script lang="ts"> | 30 | <script lang="ts"> |
18 | - import { defineComponent, ref, watch, unref } from 'vue'; | ||
19 | - import { useModal } from '/@/components/Modal'; | 31 | + import { defineComponent, ref, watch, unref, computed } from 'vue'; |
32 | + | ||
20 | import UploadModal from './UploadModal.vue'; | 33 | import UploadModal from './UploadModal.vue'; |
21 | - import { uploadContainerProps } from './props'; | ||
22 | import UploadPreviewModal from './UploadPreviewModal.vue'; | 34 | import UploadPreviewModal from './UploadPreviewModal.vue'; |
23 | - import Icon from '/@/components/Icon/index'; | 35 | + import Icon from '/@/components/Icon'; |
36 | + import { Tooltip } from 'ant-design-vue'; | ||
37 | + | ||
38 | + import { useModal } from '/@/components/Modal'; | ||
39 | + | ||
40 | + import { uploadContainerProps } from './props'; | ||
41 | + import { omit } from 'lodash-es'; | ||
42 | + | ||
24 | export default defineComponent({ | 43 | export default defineComponent({ |
25 | - components: { UploadModal, UploadPreviewModal, Icon }, | 44 | + components: { UploadModal, UploadPreviewModal, Icon, Tooltip }, |
26 | props: uploadContainerProps, | 45 | props: uploadContainerProps, |
27 | - setup(props, { emit }) { | 46 | + setup(props, { emit, attrs }) { |
28 | // 上传modal | 47 | // 上传modal |
29 | const [registerUploadModal, { openModal: openUploadModal }] = useModal(); | 48 | const [registerUploadModal, { openModal: openUploadModal }] = useModal(); |
49 | + | ||
30 | // 预览modal | 50 | // 预览modal |
31 | const [registerPreviewModal, { openModal: openPreviewModal }] = useModal(); | 51 | const [registerPreviewModal, { openModal: openPreviewModal }] = useModal(); |
32 | 52 | ||
33 | const fileListRef = ref<string[]>([]); | 53 | const fileListRef = ref<string[]>([]); |
54 | + | ||
55 | + const showPreview = computed(() => { | ||
56 | + const { emptyHidePreview } = props; | ||
57 | + if (!emptyHidePreview) return true; | ||
58 | + return emptyHidePreview ? fileListRef.value.length > 0 : true; | ||
59 | + }); | ||
60 | + | ||
61 | + const bindValue = computed(() => { | ||
62 | + const value = { ...attrs, ...props }; | ||
63 | + return omit(value, 'onChange'); | ||
64 | + }); | ||
65 | + | ||
34 | watch( | 66 | watch( |
35 | () => props.value, | 67 | () => props.value, |
36 | - (value) => { | ||
37 | - fileListRef.value = [...(value || [])]; | 68 | + (value = []) => { |
69 | + fileListRef.value = value; | ||
38 | }, | 70 | }, |
39 | { immediate: true } | 71 | { immediate: true } |
40 | ); | 72 | ); |
73 | + | ||
41 | // 上传modal保存操作 | 74 | // 上传modal保存操作 |
42 | function handleChange(urls: string[]) { | 75 | function handleChange(urls: string[]) { |
43 | fileListRef.value = [...unref(fileListRef), ...(urls || [])]; | 76 | fileListRef.value = [...unref(fileListRef), ...(urls || [])]; |
44 | emit('change', fileListRef.value); | 77 | emit('change', fileListRef.value); |
45 | } | 78 | } |
79 | + | ||
46 | // 预览modal保存操作 | 80 | // 预览modal保存操作 |
47 | function handlePreviewChange(urls: string[]) { | 81 | function handlePreviewChange(urls: string[]) { |
48 | fileListRef.value = [...(urls || [])]; | 82 | fileListRef.value = [...(urls || [])]; |
49 | emit('change', fileListRef.value); | 83 | emit('change', fileListRef.value); |
50 | } | 84 | } |
85 | + | ||
51 | return { | 86 | return { |
52 | registerUploadModal, | 87 | registerUploadModal, |
53 | openUploadModal, | 88 | openUploadModal, |
@@ -56,6 +91,8 @@ | @@ -56,6 +91,8 @@ | ||
56 | registerPreviewModal, | 91 | registerPreviewModal, |
57 | openPreviewModal, | 92 | openPreviewModal, |
58 | fileListRef, | 93 | fileListRef, |
94 | + showPreview, | ||
95 | + bindValue, | ||
59 | }; | 96 | }; |
60 | }, | 97 | }, |
61 | }); | 98 | }); |
src/components/Upload/src/ThumnUrl.vue
@@ -5,25 +5,22 @@ | @@ -5,25 +5,22 @@ | ||
5 | </span> | 5 | </span> |
6 | </template> | 6 | </template> |
7 | <script lang="ts"> | 7 | <script lang="ts"> |
8 | - import { defineComponent } from 'vue'; | 8 | + import { defineComponent, PropType } from 'vue'; |
9 | 9 | ||
10 | export default defineComponent({ | 10 | export default defineComponent({ |
11 | props: { | 11 | props: { |
12 | fileUrl: { | 12 | fileUrl: { |
13 | - type: String, | 13 | + type: String as PropType<string>, |
14 | default: '', | 14 | default: '', |
15 | }, | 15 | }, |
16 | fileType: { | 16 | fileType: { |
17 | - type: String, | 17 | + type: String as PropType<string>, |
18 | default: '', | 18 | default: '', |
19 | }, | 19 | }, |
20 | fileName: { | 20 | fileName: { |
21 | - type: String, | 21 | + type: String as PropType<string>, |
22 | default: '', | 22 | default: '', |
23 | }, | 23 | }, |
24 | }, | 24 | }, |
25 | - setup() { | ||
26 | - return {}; | ||
27 | - }, | ||
28 | }); | 25 | }); |
29 | </script> | 26 | </script> |
src/components/Upload/src/UploadModal.vue
1 | <template> | 1 | <template> |
2 | <BasicModal | 2 | <BasicModal |
3 | + width="800px" | ||
4 | + title="上传" | ||
5 | + okText="保存" | ||
3 | v-bind="$attrs" | 6 | v-bind="$attrs" |
4 | @register="register" | 7 | @register="register" |
5 | @ok="handleOk" | 8 | @ok="handleOk" |
6 | :closeFunc="handleCloseFunc" | 9 | :closeFunc="handleCloseFunc" |
7 | :maskClosable="false" | 10 | :maskClosable="false" |
8 | - width="800px" | ||
9 | - title="上传组件" | 11 | + :keyboard="false" |
10 | wrapClassName="upload-modal" | 12 | wrapClassName="upload-modal" |
11 | - :okButtonProps="{ disabled: isUploadingRef }" | 13 | + :okButtonProps="getOkButtonProps" |
12 | :cancelButtonProps="{ disabled: isUploadingRef }" | 14 | :cancelButtonProps="{ disabled: isUploadingRef }" |
13 | > | 15 | > |
14 | <template #centerdFooter> | 16 | <template #centerdFooter> |
15 | - <a-button @click="handleStartUpload" color="success" :loading="isUploadingRef"> | ||
16 | - {{ isUploadingRef ? '上传中' : '开始上传' }} | 17 | + <a-button |
18 | + @click="handleStartUpload" | ||
19 | + color="success" | ||
20 | + :disabled="!getIsSelectFile" | ||
21 | + :loading="isUploadingRef" | ||
22 | + > | ||
23 | + {{ getUploadBtnText }} | ||
17 | </a-button> | 24 | </a-button> |
18 | </template> | 25 | </template> |
19 | - <Upload :accept="getStringAccept" :multiple="multiple" :before-upload="beforeUpload"> | ||
20 | - <a-button type="primary"> 选择文件 </a-button> | ||
21 | - <span class="px-2">{{ getHelpText }}</span> | ||
22 | - </Upload> | ||
23 | - <BasicTable @register="registerTable" :dataSource="fileListRef" /> | 26 | + |
27 | + <BasicTable @register="registerTable" :dataSource="fileListRef"> | ||
28 | + <template #toolbar> | ||
29 | + <Upload :accept="getStringAccept" :multiple="multiple" :before-upload="beforeUpload"> | ||
30 | + <a-button type="primary"> 选择文件 </a-button> | ||
31 | + </Upload> | ||
32 | + </template> | ||
33 | + <template #tableTitle> | ||
34 | + <Alert :message="getHelpText" type="info" banner></Alert> | ||
35 | + </template> | ||
36 | + </BasicTable> | ||
24 | </BasicModal> | 37 | </BasicModal> |
25 | </template> | 38 | </template> |
26 | <script lang="ts"> | 39 | <script lang="ts"> |
27 | - import { defineComponent, reactive, ref, toRef, unref } from 'vue'; | ||
28 | - import { Upload } from 'ant-design-vue'; | 40 | + import { defineComponent, reactive, ref, toRefs, unref, computed } from 'vue'; |
41 | + import { Upload, Alert } from 'ant-design-vue'; | ||
29 | import { BasicModal, useModalInner } from '/@/components/Modal'; | 42 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
30 | import { BasicTable, useTable } from '/@/components/Table'; | 43 | import { BasicTable, useTable } from '/@/components/Table'; |
31 | // hooks | 44 | // hooks |
@@ -39,23 +52,56 @@ | @@ -39,23 +52,56 @@ | ||
39 | import { checkFileType, checkImgType, getBase64WithFile } from './utils'; | 52 | import { checkFileType, checkImgType, getBase64WithFile } from './utils'; |
40 | import { buildUUID } from '/@/utils/uuid'; | 53 | import { buildUUID } from '/@/utils/uuid'; |
41 | import { createImgPreview } from '/@/components/Preview/index'; | 54 | import { createImgPreview } from '/@/components/Preview/index'; |
42 | - import { uploadApi } from '/@/api/demo/upload'; | 55 | + import { uploadApi } from '/@/api/sys/upload'; |
56 | + import { isFunction } from '/@/utils/is'; | ||
57 | + import { warn } from '/@/utils/log'; | ||
43 | 58 | ||
44 | export default defineComponent({ | 59 | export default defineComponent({ |
45 | - components: { BasicModal, Upload, BasicTable }, | 60 | + components: { BasicModal, Upload, BasicTable, Alert }, |
46 | props: basicProps, | 61 | props: basicProps, |
47 | setup(props, { emit }) { | 62 | setup(props, { emit }) { |
63 | + // 是否正在上传 | ||
64 | + const isUploadingRef = ref(false); | ||
65 | + const fileListRef = ref<FileItem[]>([]); | ||
66 | + const state = reactive<{ fileList: FileItem[] }>({ | ||
67 | + fileList: [], | ||
68 | + }); | ||
69 | + | ||
48 | const [register, { closeModal }] = useModalInner(); | 70 | const [register, { closeModal }] = useModalInner(); |
71 | + | ||
72 | + const { accept, helpText, maxNumber, maxSize } = toRefs(props); | ||
49 | const { getAccept, getStringAccept, getHelpText } = useUploadType({ | 73 | const { getAccept, getStringAccept, getHelpText } = useUploadType({ |
50 | - acceptRef: toRef(props, 'accept'), | ||
51 | - helpTextRef: toRef(props, 'helpText'), | ||
52 | - maxNumberRef: toRef(props, 'maxNumber'), | ||
53 | - maxSizeRef: toRef(props, 'maxSize'), | 74 | + acceptRef: accept, |
75 | + helpTextRef: helpText, | ||
76 | + maxNumberRef: maxNumber, | ||
77 | + maxSizeRef: maxSize, | ||
54 | }); | 78 | }); |
55 | 79 | ||
56 | - const fileListRef = ref<FileItem[]>([]); | ||
57 | - const state = reactive<{ fileList: FileItem[] }>({ fileList: [] }); | ||
58 | const { createMessage } = useMessage(); | 80 | const { createMessage } = useMessage(); |
81 | + | ||
82 | + const getIsSelectFile = computed(() => { | ||
83 | + return ( | ||
84 | + fileListRef.value.length > 0 && | ||
85 | + !fileListRef.value.every((item) => item.status === UploadResultStatus.SUCCESS) | ||
86 | + ); | ||
87 | + }); | ||
88 | + | ||
89 | + const getOkButtonProps = computed(() => { | ||
90 | + const someSuccess = fileListRef.value.some( | ||
91 | + (item) => item.status === UploadResultStatus.SUCCESS | ||
92 | + ); | ||
93 | + return { | ||
94 | + disabled: isUploadingRef.value || fileListRef.value.length === 0 || !someSuccess, | ||
95 | + }; | ||
96 | + }); | ||
97 | + | ||
98 | + const getUploadBtnText = computed(() => { | ||
99 | + const someError = fileListRef.value.some( | ||
100 | + (item) => item.status === UploadResultStatus.ERROR | ||
101 | + ); | ||
102 | + return isUploadingRef.value ? '上传中' : someError ? '重新上传失败文件' : '开始上传'; | ||
103 | + }); | ||
104 | + | ||
59 | // 上传前校验 | 105 | // 上传前校验 |
60 | function beforeUpload(file: File) { | 106 | function beforeUpload(file: File) { |
61 | const { size, name } = file; | 107 | const { size, name } = file; |
@@ -73,6 +119,14 @@ | @@ -73,6 +119,14 @@ | ||
73 | createMessage.error!(`只能上传${accept.join(',')}格式文件`); | 119 | createMessage.error!(`只能上传${accept.join(',')}格式文件`); |
74 | return false; | 120 | return false; |
75 | } | 121 | } |
122 | + const commonItem = { | ||
123 | + uuid: buildUUID(), | ||
124 | + file, | ||
125 | + size, | ||
126 | + name, | ||
127 | + percent: 0, | ||
128 | + type: name.split('.').pop(), | ||
129 | + }; | ||
76 | // 生成图片缩略图 | 130 | // 生成图片缩略图 |
77 | if (checkImgType(file)) { | 131 | if (checkImgType(file)) { |
78 | // beforeUpload,如果异步会调用自带上传方法 | 132 | // beforeUpload,如果异步会调用自带上传方法 |
@@ -81,29 +135,13 @@ | @@ -81,29 +135,13 @@ | ||
81 | fileListRef.value = [ | 135 | fileListRef.value = [ |
82 | ...unref(fileListRef), | 136 | ...unref(fileListRef), |
83 | { | 137 | { |
84 | - uuid: buildUUID(), | ||
85 | - file, | ||
86 | thumbUrl, | 138 | thumbUrl, |
87 | - size, | ||
88 | - name, | ||
89 | - percent: 0, | ||
90 | - type: name.split('.').pop(), | 139 | + ...commonItem, |
91 | }, | 140 | }, |
92 | ]; | 141 | ]; |
93 | }); | 142 | }); |
94 | } else { | 143 | } else { |
95 | - fileListRef.value = [ | ||
96 | - ...unref(fileListRef), | ||
97 | - { | ||
98 | - uuid: buildUUID(), | ||
99 | - | ||
100 | - file, | ||
101 | - size, | ||
102 | - name, | ||
103 | - percent: 0, | ||
104 | - type: name.split('.').pop(), | ||
105 | - }, | ||
106 | - ]; | 144 | + fileListRef.value = [...unref(fileListRef), commonItem]; |
107 | } | 145 | } |
108 | return false; | 146 | return false; |
109 | } | 147 | } |
@@ -112,6 +150,7 @@ | @@ -112,6 +150,7 @@ | ||
112 | const index = fileListRef.value.findIndex((item) => item.uuid === record.uuid); | 150 | const index = fileListRef.value.findIndex((item) => item.uuid === record.uuid); |
113 | index !== -1 && fileListRef.value.splice(index, 1); | 151 | index !== -1 && fileListRef.value.splice(index, 1); |
114 | } | 152 | } |
153 | + | ||
115 | // 预览 | 154 | // 预览 |
116 | function handlePreview(record: FileItem) { | 155 | function handlePreview(record: FileItem) { |
117 | const { thumbUrl = '' } = record; | 156 | const { thumbUrl = '' } = record; |
@@ -119,19 +158,18 @@ | @@ -119,19 +158,18 @@ | ||
119 | imageList: [thumbUrl], | 158 | imageList: [thumbUrl], |
120 | }); | 159 | }); |
121 | } | 160 | } |
122 | - const [registerTable] = useTable({ | ||
123 | - columns: createTableColumns(), | ||
124 | - actionColumn: createActionColumn(handleRemove, handlePreview), | ||
125 | - pagination: false, | ||
126 | - }); | ||
127 | - // 是否正在上传 | ||
128 | - const isUploadingRef = ref(false); | 161 | + |
129 | async function uploadApiByItem(item: FileItem) { | 162 | async function uploadApiByItem(item: FileItem) { |
163 | + const { api } = props; | ||
164 | + if (!api || !isFunction(api)) { | ||
165 | + return warn('upload api must exist and be a function'); | ||
166 | + } | ||
130 | try { | 167 | try { |
131 | item.status = UploadResultStatus.UPLOADING; | 168 | item.status = UploadResultStatus.UPLOADING; |
132 | 169 | ||
133 | const { data } = await uploadApi( | 170 | const { data } = await uploadApi( |
134 | { | 171 | { |
172 | + ...(props.uploadParams || {}), | ||
135 | file: item.file, | 173 | file: item.file, |
136 | }, | 174 | }, |
137 | function onUploadProgress(progressEvent: ProgressEvent) { | 175 | function onUploadProgress(progressEvent: ProgressEvent) { |
@@ -154,32 +192,42 @@ | @@ -154,32 +192,42 @@ | ||
154 | }; | 192 | }; |
155 | } | 193 | } |
156 | } | 194 | } |
195 | + | ||
157 | // 点击开始上传 | 196 | // 点击开始上传 |
158 | async function handleStartUpload() { | 197 | async function handleStartUpload() { |
198 | + const { maxNumber } = props; | ||
199 | + if (fileListRef.value.length > maxNumber) { | ||
200 | + return createMessage.warning(`最多只能上传${maxNumber}个文件`); | ||
201 | + } | ||
159 | try { | 202 | try { |
160 | isUploadingRef.value = true; | 203 | isUploadingRef.value = true; |
204 | + // 只上传不是成功状态的 | ||
205 | + const uploadFileList = | ||
206 | + fileListRef.value.filter((item) => item.status !== UploadResultStatus.SUCCESS) || []; | ||
161 | const data = await Promise.all( | 207 | const data = await Promise.all( |
162 | - unref(fileListRef).map((item) => { | 208 | + uploadFileList.map((item) => { |
163 | return uploadApiByItem(item); | 209 | return uploadApiByItem(item); |
164 | }) | 210 | }) |
165 | ); | 211 | ); |
166 | isUploadingRef.value = false; | 212 | isUploadingRef.value = false; |
167 | // 生产环境:抛出错误 | 213 | // 生产环境:抛出错误 |
168 | - const errorList = data.filter((item) => !item.success); | ||
169 | - if (errorList.length > 0) { | ||
170 | - throw errorList; | ||
171 | - } | 214 | + const errorList = data.filter((item: any) => !item.success); |
215 | + if (errorList.length > 0) throw errorList; | ||
172 | } catch (e) { | 216 | } catch (e) { |
173 | isUploadingRef.value = false; | 217 | isUploadingRef.value = false; |
174 | throw e; | 218 | throw e; |
175 | } | 219 | } |
176 | } | 220 | } |
221 | + | ||
177 | // 点击保存 | 222 | // 点击保存 |
178 | function handleOk() { | 223 | function handleOk() { |
179 | - // TODO: 没起作用:okButtonProps={{ disabled: state.isUploading }} | 224 | + const { maxNumber } = props; |
225 | + | ||
226 | + if (fileListRef.value.length > maxNumber) { | ||
227 | + return createMessage.warning(`最多只能上传${maxNumber}个文件`); | ||
228 | + } | ||
180 | if (isUploadingRef.value) { | 229 | if (isUploadingRef.value) { |
181 | - createMessage.warning('请等待文件上传后,保存'); | ||
182 | - return; | 230 | + return createMessage.warning('请等待文件上传后,保存'); |
183 | } | 231 | } |
184 | const fileList: string[] = []; | 232 | const fileList: string[] = []; |
185 | 233 | ||
@@ -189,18 +237,15 @@ | @@ -189,18 +237,15 @@ | ||
189 | fileList.push(responseData.url); | 237 | fileList.push(responseData.url); |
190 | } | 238 | } |
191 | } | 239 | } |
192 | - | ||
193 | // 存在一个上传成功的即可保存 | 240 | // 存在一个上传成功的即可保存 |
194 | - | ||
195 | if (fileList.length <= 0) { | 241 | if (fileList.length <= 0) { |
196 | - createMessage.warning('没有上传成功的文件,无法保存'); | ||
197 | - return; | 242 | + return createMessage.warning('没有上传成功的文件,无法保存'); |
198 | } | 243 | } |
199 | - console.log(fileList); | ||
200 | - emit('change', fileList); | ||
201 | fileListRef.value = []; | 244 | fileListRef.value = []; |
202 | closeModal(); | 245 | closeModal(); |
246 | + emit('change', fileList); | ||
203 | } | 247 | } |
248 | + | ||
204 | // 点击关闭:则所有操作不保存,包括上传的 | 249 | // 点击关闭:则所有操作不保存,包括上传的 |
205 | function handleCloseFunc() { | 250 | function handleCloseFunc() { |
206 | if (!isUploadingRef.value) { | 251 | if (!isUploadingRef.value) { |
@@ -211,11 +256,22 @@ | @@ -211,11 +256,22 @@ | ||
211 | return false; | 256 | return false; |
212 | } | 257 | } |
213 | } | 258 | } |
259 | + | ||
260 | + const [registerTable] = useTable({ | ||
261 | + columns: createTableColumns(), | ||
262 | + actionColumn: createActionColumn(handleRemove, handlePreview), | ||
263 | + pagination: false, | ||
264 | + inset: true, | ||
265 | + scroll: { | ||
266 | + y: 3000, | ||
267 | + }, | ||
268 | + }); | ||
214 | return { | 269 | return { |
215 | register, | 270 | register, |
216 | closeModal, | 271 | closeModal, |
217 | getHelpText, | 272 | getHelpText, |
218 | getStringAccept, | 273 | getStringAccept, |
274 | + getOkButtonProps, | ||
219 | beforeUpload, | 275 | beforeUpload, |
220 | registerTable, | 276 | registerTable, |
221 | fileListRef, | 277 | fileListRef, |
@@ -224,14 +280,13 @@ | @@ -224,14 +280,13 @@ | ||
224 | handleStartUpload, | 280 | handleStartUpload, |
225 | handleOk, | 281 | handleOk, |
226 | handleCloseFunc, | 282 | handleCloseFunc, |
283 | + getIsSelectFile, | ||
284 | + getUploadBtnText, | ||
227 | }; | 285 | }; |
228 | }, | 286 | }, |
229 | }); | 287 | }); |
230 | </script> | 288 | </script> |
231 | <style lang="less"> | 289 | <style lang="less"> |
232 | - // /deep/ .ant-upload-list { | ||
233 | - // display: none; | ||
234 | - // } | ||
235 | .upload-modal { | 290 | .upload-modal { |
236 | .ant-upload-list { | 291 | .ant-upload-list { |
237 | display: none; | 292 | display: none; |
src/components/Upload/src/UploadPreviewModal.vue
1 | <template> | 1 | <template> |
2 | <BasicModal | 2 | <BasicModal |
3 | + width="800px" | ||
4 | + title="预览" | ||
3 | wrapClassName="upload-preview-modal" | 5 | wrapClassName="upload-preview-modal" |
4 | v-bind="$attrs" | 6 | v-bind="$attrs" |
5 | - width="800px" | ||
6 | @register="register" | 7 | @register="register" |
7 | - title="预览" | ||
8 | :showOkBtn="false" | 8 | :showOkBtn="false" |
9 | > | 9 | > |
10 | <BasicTable @register="registerTable" :dataSource="fileListRef" /> | 10 | <BasicTable @register="registerTable" :dataSource="fileListRef" /> |
@@ -12,17 +12,18 @@ | @@ -12,17 +12,18 @@ | ||
12 | </template> | 12 | </template> |
13 | <script lang="ts"> | 13 | <script lang="ts"> |
14 | import { defineComponent, watch, ref, unref } from 'vue'; | 14 | import { defineComponent, watch, ref, unref } from 'vue'; |
15 | + | ||
15 | import { BasicTable, useTable } from '/@/components/Table'; | 16 | import { BasicTable, useTable } from '/@/components/Table'; |
16 | - import { createPreviewColumns, createPreviewActionColumn } from './data'; | ||
17 | import { BasicModal, useModalInner } from '/@/components/Modal'; | 17 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
18 | - import { priviewProps } from './props'; | 18 | + import { previewProps } from './props'; |
19 | import { PreviewFileItem } from './types'; | 19 | import { PreviewFileItem } from './types'; |
20 | import { createImgPreview } from '/@/components/Preview/index'; | 20 | import { createImgPreview } from '/@/components/Preview/index'; |
21 | - import { downloadByUrl } from '/@/utils/file/FileDownload'; | 21 | + import { downloadByUrl } from '/@/utils/file/download'; |
22 | 22 | ||
23 | + import { createPreviewColumns, createPreviewActionColumn } from './data'; | ||
23 | export default defineComponent({ | 24 | export default defineComponent({ |
24 | components: { BasicModal, BasicTable }, | 25 | components: { BasicModal, BasicTable }, |
25 | - props: priviewProps, | 26 | + props: previewProps, |
26 | setup(props, { emit }) { | 27 | setup(props, { emit }) { |
27 | const [register, { closeModal }] = useModalInner(); | 28 | const [register, { closeModal }] = useModalInner(); |
28 | const fileListRef = ref<PreviewFileItem[]>([]); | 29 | const fileListRef = ref<PreviewFileItem[]>([]); |
@@ -43,17 +44,19 @@ | @@ -43,17 +44,19 @@ | ||
43 | }, | 44 | }, |
44 | { immediate: true } | 45 | { immediate: true } |
45 | ); | 46 | ); |
47 | + | ||
46 | // 删除 | 48 | // 删除 |
47 | function handleRemove(record: PreviewFileItem) { | 49 | function handleRemove(record: PreviewFileItem) { |
48 | const index = fileListRef.value.findIndex((item) => item.url === record.url); | 50 | const index = fileListRef.value.findIndex((item) => item.url === record.url); |
49 | if (index !== -1) { | 51 | if (index !== -1) { |
50 | fileListRef.value.splice(index, 1); | 52 | fileListRef.value.splice(index, 1); |
51 | emit( | 53 | emit( |
52 | - 'change', | 54 | + 'list-change', |
53 | fileListRef.value.map((item) => item.url) | 55 | fileListRef.value.map((item) => item.url) |
54 | ); | 56 | ); |
55 | } | 57 | } |
56 | } | 58 | } |
59 | + | ||
57 | // 预览 | 60 | // 预览 |
58 | function handlePreview(record: PreviewFileItem) { | 61 | function handlePreview(record: PreviewFileItem) { |
59 | const { url = '' } = record; | 62 | const { url = '' } = record; |
@@ -61,16 +64,19 @@ | @@ -61,16 +64,19 @@ | ||
61 | imageList: [url], | 64 | imageList: [url], |
62 | }); | 65 | }); |
63 | } | 66 | } |
67 | + | ||
64 | // 下载 | 68 | // 下载 |
65 | function handleDownload(record: PreviewFileItem) { | 69 | function handleDownload(record: PreviewFileItem) { |
66 | const { url = '' } = record; | 70 | const { url = '' } = record; |
67 | downloadByUrl({ url }); | 71 | downloadByUrl({ url }); |
68 | } | 72 | } |
73 | + | ||
69 | const [registerTable] = useTable({ | 74 | const [registerTable] = useTable({ |
70 | columns: createPreviewColumns(), | 75 | columns: createPreviewColumns(), |
71 | pagination: false, | 76 | pagination: false, |
72 | actionColumn: createPreviewActionColumn({ handleRemove, handlePreview, handleDownload }), | 77 | actionColumn: createPreviewActionColumn({ handleRemove, handlePreview, handleDownload }), |
73 | }); | 78 | }); |
79 | + | ||
74 | return { | 80 | return { |
75 | register, | 81 | register, |
76 | closeModal, | 82 | closeModal, |
src/components/Upload/src/data.tsx
1 | -// import { BasicColumn, TableAction, ActionItem } from '@/components/table'; | ||
2 | import { checkImgType, isImgTypeByName } from './utils'; | 1 | import { checkImgType, isImgTypeByName } from './utils'; |
3 | -// import ThumnUrl from './ThumbUrl.vue'; | ||
4 | -import { Progress } from 'ant-design-vue'; | 2 | +import { Progress, Tag } from 'ant-design-vue'; |
5 | import { FileItem, PreviewFileItem, UploadResultStatus } from './types'; | 3 | import { FileItem, PreviewFileItem, UploadResultStatus } from './types'; |
6 | -// import { ElecArchivesSaveResult } from '@/api/biz/file/model/fileModel'; | ||
7 | -// import { quryFile } from '@/api/biz/file/file'; | ||
8 | import { BasicColumn, ActionItem, TableAction } from '/@/components/Table/index'; | 4 | import { BasicColumn, ActionItem, TableAction } from '/@/components/Table/index'; |
9 | 5 | ||
10 | // 文件上传列表 | 6 | // 文件上传列表 |
@@ -16,8 +12,7 @@ export function createTableColumns(): BasicColumn[] { | @@ -16,8 +12,7 @@ export function createTableColumns(): BasicColumn[] { | ||
16 | width: 100, | 12 | width: 100, |
17 | customRender: ({ record }) => { | 13 | customRender: ({ record }) => { |
18 | const { thumbUrl, type } = (record as FileItem) || {}; | 14 | const { thumbUrl, type } = (record as FileItem) || {}; |
19 | - return <span>{thumbUrl ? <img src={thumbUrl} style={{ width: '50px' }} /> : type}</span>; | ||
20 | - // return <ThumnUrl fileUrl={thumbUrl} fileType={type} fileName={type} />; | 15 | + return <span>{thumbUrl ? <img style={{ maxWidth: '60px' }} src={thumbUrl} /> : type}</span>; |
21 | }, | 16 | }, |
22 | }, | 17 | }, |
23 | { | 18 | { |
@@ -26,7 +21,7 @@ export function createTableColumns(): BasicColumn[] { | @@ -26,7 +21,7 @@ export function createTableColumns(): BasicColumn[] { | ||
26 | align: 'left', | 21 | align: 'left', |
27 | customRender: ({ text, record }) => { | 22 | customRender: ({ text, record }) => { |
28 | const { percent, status: uploadStatus } = (record as FileItem) || {}; | 23 | const { percent, status: uploadStatus } = (record as FileItem) || {}; |
29 | - let status = 'normal'; | 24 | + let status: 'normal' | 'exception' | 'active' | 'success' = 'normal'; |
30 | if (uploadStatus === UploadResultStatus.ERROR) { | 25 | if (uploadStatus === UploadResultStatus.ERROR) { |
31 | status = 'exception'; | 26 | status = 'exception'; |
32 | } else if (uploadStatus === UploadResultStatus.UPLOADING) { | 27 | } else if (uploadStatus === UploadResultStatus.UPLOADING) { |
@@ -63,11 +58,11 @@ export function createTableColumns(): BasicColumn[] { | @@ -63,11 +58,11 @@ export function createTableColumns(): BasicColumn[] { | ||
63 | width: 100, | 58 | width: 100, |
64 | customRender: ({ text }) => { | 59 | customRender: ({ text }) => { |
65 | if (text === UploadResultStatus.SUCCESS) { | 60 | if (text === UploadResultStatus.SUCCESS) { |
66 | - return '上传成功'; | 61 | + return <Tag color="green">{() => '上传成功'}</Tag>; |
67 | } else if (text === UploadResultStatus.ERROR) { | 62 | } else if (text === UploadResultStatus.ERROR) { |
68 | - return '上传失败'; | 63 | + return <Tag color="red">{() => '上传失败'}</Tag>; |
69 | } else if (text === UploadResultStatus.UPLOADING) { | 64 | } else if (text === UploadResultStatus.UPLOADING) { |
70 | - return '上传中'; | 65 | + return <Tag color="blue">{() => '上传中'}</Tag>; |
71 | } | 66 | } |
72 | 67 | ||
73 | return text; | 68 | return text; |
@@ -85,6 +80,7 @@ export function createActionColumn(handleRemove: Function, handlePreview: Functi | @@ -85,6 +80,7 @@ export function createActionColumn(handleRemove: Function, handlePreview: Functi | ||
85 | const actions: ActionItem[] = [ | 80 | const actions: ActionItem[] = [ |
86 | { | 81 | { |
87 | label: '删除', | 82 | label: '删除', |
83 | + color: 'error', | ||
88 | onClick: handleRemove.bind(null, record), | 84 | onClick: handleRemove.bind(null, record), |
89 | }, | 85 | }, |
90 | ]; | 86 | ]; |
@@ -125,9 +121,9 @@ export function createPreviewActionColumn({ | @@ -125,9 +121,9 @@ export function createPreviewActionColumn({ | ||
125 | handlePreview, | 121 | handlePreview, |
126 | handleDownload, | 122 | handleDownload, |
127 | }: { | 123 | }: { |
128 | - handleRemove: Function; | ||
129 | - handlePreview: Function; | ||
130 | - handleDownload: Function; | 124 | + handleRemove: Fn; |
125 | + handlePreview: Fn; | ||
126 | + handleDownload: Fn; | ||
131 | }): BasicColumn { | 127 | }): BasicColumn { |
132 | return { | 128 | return { |
133 | width: 160, | 129 | width: 160, |
@@ -135,11 +131,12 @@ export function createPreviewActionColumn({ | @@ -135,11 +131,12 @@ export function createPreviewActionColumn({ | ||
135 | dataIndex: 'action', | 131 | dataIndex: 'action', |
136 | fixed: false, | 132 | fixed: false, |
137 | customRender: ({ record }) => { | 133 | customRender: ({ record }) => { |
138 | - const { url } = (record as PreviewFileItem) || {}; | 134 | + const { url } = (record || {}) as PreviewFileItem; |
139 | 135 | ||
140 | const actions: ActionItem[] = [ | 136 | const actions: ActionItem[] = [ |
141 | { | 137 | { |
142 | label: '删除', | 138 | label: '删除', |
139 | + color: 'error', | ||
143 | onClick: handleRemove.bind(null, record), | 140 | onClick: handleRemove.bind(null, record), |
144 | }, | 141 | }, |
145 | { | 142 | { |
src/components/Upload/src/props.ts
@@ -10,10 +10,10 @@ export const basicProps = { | @@ -10,10 +10,10 @@ export const basicProps = { | ||
10 | type: Number as PropType<number>, | 10 | type: Number as PropType<number>, |
11 | default: 2, | 11 | default: 2, |
12 | }, | 12 | }, |
13 | - // 最大数量的文件,0不限制 | 13 | + // 最大数量的文件,Infinity不限制 |
14 | maxNumber: { | 14 | maxNumber: { |
15 | type: Number as PropType<number>, | 15 | type: Number as PropType<number>, |
16 | - default: 0, | 16 | + default: Infinity, |
17 | }, | 17 | }, |
18 | // 根据后缀,或者其他 | 18 | // 根据后缀,或者其他 |
19 | accept: { | 19 | accept: { |
@@ -21,9 +21,18 @@ export const basicProps = { | @@ -21,9 +21,18 @@ export const basicProps = { | ||
21 | default: () => [], | 21 | default: () => [], |
22 | }, | 22 | }, |
23 | multiple: { | 23 | multiple: { |
24 | - type: Boolean, | 24 | + type: Boolean as PropType<boolean>, |
25 | default: true, | 25 | default: true, |
26 | }, | 26 | }, |
27 | + uploadParams: { | ||
28 | + type: Object as PropType<any>, | ||
29 | + default: {}, | ||
30 | + }, | ||
31 | + api: { | ||
32 | + type: Function as PropType<PromiseFn>, | ||
33 | + default: null, | ||
34 | + required: true, | ||
35 | + }, | ||
27 | }; | 36 | }; |
28 | 37 | ||
29 | export const uploadContainerProps = { | 38 | export const uploadContainerProps = { |
@@ -32,9 +41,17 @@ export const uploadContainerProps = { | @@ -32,9 +41,17 @@ export const uploadContainerProps = { | ||
32 | default: () => [], | 41 | default: () => [], |
33 | }, | 42 | }, |
34 | ...basicProps, | 43 | ...basicProps, |
44 | + showPreviewNumber: { | ||
45 | + type: Boolean as PropType<boolean>, | ||
46 | + default: true, | ||
47 | + }, | ||
48 | + emptyHidePreview: { | ||
49 | + type: Boolean as PropType<boolean>, | ||
50 | + default: false, | ||
51 | + }, | ||
35 | }; | 52 | }; |
36 | 53 | ||
37 | -export const priviewProps = { | 54 | +export const previewProps = { |
38 | value: { | 55 | value: { |
39 | type: Array as PropType<string[]>, | 56 | type: Array as PropType<string[]>, |
40 | default: () => [], | 57 | default: () => [], |
src/components/Upload/src/types.ts
src/components/Upload/src/useUpload.ts
@@ -42,12 +42,12 @@ export function useUploadType({ | @@ -42,12 +42,12 @@ export function useUploadType({ | ||
42 | 42 | ||
43 | const maxSize = unref(maxSizeRef); | 43 | const maxSize = unref(maxSizeRef); |
44 | if (maxSize) { | 44 | if (maxSize) { |
45 | - helpTexts.push(`不超过${maxSize}MB`); | 45 | + helpTexts.push(`单个文件不超过${maxSize}MB`); |
46 | } | 46 | } |
47 | 47 | ||
48 | const maxNumber = unref(maxNumberRef); | 48 | const maxNumber = unref(maxNumberRef); |
49 | - if (maxNumber) { | ||
50 | - helpTexts.push(`最多可选择${maxNumber}个文件`); | 49 | + if (maxNumber && maxNumber !== Infinity) { |
50 | + helpTexts.push(`最多只能上传${maxNumber}个文件`); | ||
51 | } | 51 | } |
52 | return helpTexts.join(','); | 52 | return helpTexts.join(','); |
53 | }); | 53 | }); |
src/components/Upload/src/utils.ts
@@ -3,18 +3,17 @@ export function checkFileType(file: File, accepts: string[]) { | @@ -3,18 +3,17 @@ export function checkFileType(file: File, accepts: string[]) { | ||
3 | // const reg = /\.(jpg|jpeg|png|gif|txt|doc|docx|xls|xlsx|xml)$/i; | 3 | // const reg = /\.(jpg|jpeg|png|gif|txt|doc|docx|xls|xlsx|xml)$/i; |
4 | const reg = new RegExp('\\.(' + newTypes + ')$', 'i'); | 4 | const reg = new RegExp('\\.(' + newTypes + ')$', 'i'); |
5 | 5 | ||
6 | - if (!reg.test(file.name)) { | ||
7 | - return false; | ||
8 | - } else { | ||
9 | - return true; | ||
10 | - } | 6 | + return reg.test(file.name); |
11 | } | 7 | } |
8 | + | ||
12 | export function checkImgType(file: File) { | 9 | export function checkImgType(file: File) { |
13 | - return /\.(jpg|jpeg|png|gif)$/i.test(file.name); | 10 | + return isImgTypeByName(file.name); |
14 | } | 11 | } |
12 | + | ||
15 | export function isImgTypeByName(name: string) { | 13 | export function isImgTypeByName(name: string) { |
16 | return /\.(jpg|jpeg|png|gif)$/i.test(name); | 14 | return /\.(jpg|jpeg|png|gif)$/i.test(name); |
17 | } | 15 | } |
16 | + | ||
18 | export function getBase64WithFile(file: File) { | 17 | export function getBase64WithFile(file: File) { |
19 | return new Promise<{ | 18 | return new Promise<{ |
20 | result: string; | 19 | result: string; |
src/design/ant/btn.less
@@ -6,6 +6,11 @@ | @@ -6,6 +6,11 @@ | ||
6 | // &.ant-btn-primary:not(.ant-btn-link) { | 6 | // &.ant-btn-primary:not(.ant-btn-link) { |
7 | // box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.12), 0 2px 4px 0 rgba(0, 0, 0, 0.08) !important; | 7 | // box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.12), 0 2px 4px 0 rgba(0, 0, 0, 0.08) !important; |
8 | // } | 8 | // } |
9 | + // &-group { | ||
10 | + // .ant-btn:not(:first-child) { | ||
11 | + // bottom: 1px; | ||
12 | + // } | ||
13 | + // } | ||
9 | 14 | ||
10 | &-primary { | 15 | &-primary { |
11 | color: @white; | 16 | color: @white; |
src/design/var/index.less
src/router/menus/modules/demo/comp.ts
@@ -4,6 +4,9 @@ const menu: MenuModule = { | @@ -4,6 +4,9 @@ const menu: MenuModule = { | ||
4 | menu: { | 4 | menu: { |
5 | name: '组件', | 5 | name: '组件', |
6 | path: '/comp', | 6 | path: '/comp', |
7 | + tag: { | ||
8 | + dot: true, | ||
9 | + }, | ||
7 | children: [ | 10 | children: [ |
8 | { | 11 | { |
9 | path: 'basic', | 12 | path: 'basic', |
@@ -38,10 +41,13 @@ const menu: MenuModule = { | @@ -38,10 +41,13 @@ const menu: MenuModule = { | ||
38 | path: 'strength-meter', | 41 | path: 'strength-meter', |
39 | name: '密码强度组件', | 42 | name: '密码强度组件', |
40 | }, | 43 | }, |
41 | - // { | ||
42 | - // path: 'upload', | ||
43 | - // name: '上传组件', | ||
44 | - // }, | 44 | + { |
45 | + path: 'upload', | ||
46 | + name: '上传组件', | ||
47 | + tag: { | ||
48 | + content: 'new', | ||
49 | + }, | ||
50 | + }, | ||
45 | { | 51 | { |
46 | path: 'scroll', | 52 | path: 'scroll', |
47 | name: '滚动组件', | 53 | name: '滚动组件', |
src/types/global.d.ts
src/utils/file/FileDownload.ts renamed to src/utils/file/download.ts
1 | -import { dataURLtoBlob } from './stream'; | 1 | +import { dataURLtoBlob, urlToBase64 } from './stream'; |
2 | 2 | ||
3 | +/** | ||
4 | + * Download online pictures | ||
5 | + * @param url | ||
6 | + * @param filename | ||
7 | + * @param mime | ||
8 | + * @param bom | ||
9 | + */ | ||
10 | +export function downloadByOnlineUrl(url: string, filename: string, mime?: string, bom?: BlobPart) { | ||
11 | + urlToBase64(url).then((base64) => { | ||
12 | + downloadByBase64(base64, filename, mime, bom); | ||
13 | + }); | ||
14 | +} | ||
15 | + | ||
16 | +/** | ||
17 | + * Download pictures based on base64 | ||
18 | + * @param buf | ||
19 | + * @param filename | ||
20 | + * @param mime | ||
21 | + * @param bom | ||
22 | + */ | ||
3 | export function downloadByBase64(buf: string, filename: string, mime?: string, bom?: BlobPart) { | 23 | export function downloadByBase64(buf: string, filename: string, mime?: string, bom?: BlobPart) { |
4 | const base64Buf = dataURLtoBlob(buf); | 24 | const base64Buf = dataURLtoBlob(buf); |
5 | downloadByData(base64Buf, filename, mime, bom); | 25 | downloadByData(base64Buf, filename, mime, bom); |
src/utils/file/stream.ts
@@ -13,3 +13,29 @@ export function dataURLtoBlob(base64Buf: string): Blob { | @@ -13,3 +13,29 @@ export function dataURLtoBlob(base64Buf: string): Blob { | ||
13 | } | 13 | } |
14 | return new Blob([u8arr], { type: mime }); | 14 | return new Blob([u8arr], { type: mime }); |
15 | } | 15 | } |
16 | + | ||
17 | +/** | ||
18 | + * img url to base64 | ||
19 | + * @param url | ||
20 | + */ | ||
21 | +export function urlToBase64(url: string, mineType?: string): Promise<string> { | ||
22 | + return new Promise((resolve, reject) => { | ||
23 | + let canvas = document.createElement('CANVAS') as Nullable<HTMLCanvasElement>; | ||
24 | + const ctx = canvas!.getContext('2d'); | ||
25 | + | ||
26 | + const img = new Image(); | ||
27 | + img.crossOrigin = ''; | ||
28 | + img.onload = function () { | ||
29 | + if (!canvas || !ctx) { | ||
30 | + return reject(); | ||
31 | + } | ||
32 | + canvas.height = img.height; | ||
33 | + canvas.width = img.width; | ||
34 | + ctx.drawImage(img, 0, 0); | ||
35 | + const dataURL = canvas.toDataURL(mineType || 'image/png'); | ||
36 | + canvas = null; | ||
37 | + resolve(dataURL); | ||
38 | + }; | ||
39 | + img.src = url; | ||
40 | + }); | ||
41 | +} |
src/utils/http/axios/Axios.ts
@@ -118,11 +118,8 @@ export class VAxios { | @@ -118,11 +118,8 @@ export class VAxios { | ||
118 | Object.keys(params.data).forEach((key) => { | 118 | Object.keys(params.data).forEach((key) => { |
119 | if (!params.data) return; | 119 | if (!params.data) return; |
120 | const value = params.data[key]; | 120 | const value = params.data[key]; |
121 | - // support key-value array data | ||
122 | if (Array.isArray(value)) { | 121 | if (Array.isArray(value)) { |
123 | value.forEach((item) => { | 122 | value.forEach((item) => { |
124 | - // { list: [ 11, 22 ] } | ||
125 | - // formData.append('list[]', 11); | ||
126 | formData.append(`${key}[]`, item); | 123 | formData.append(`${key}[]`, item); |
127 | }); | 124 | }); |
128 | return; | 125 | return; |
src/utils/http/axios/index.ts
@@ -160,20 +160,18 @@ const transform: AxiosTransform = { | @@ -160,20 +160,18 @@ const transform: AxiosTransform = { | ||
160 | try { | 160 | try { |
161 | if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) { | 161 | if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) { |
162 | createMessage.error('接口请求超时,请刷新页面重试!'); | 162 | createMessage.error('接口请求超时,请刷新页面重试!'); |
163 | - return; | ||
164 | } | 163 | } |
165 | if (err && err.includes('Network Error')) { | 164 | if (err && err.includes('Network Error')) { |
166 | createErrorModal({ | 165 | createErrorModal({ |
167 | title: '网络异常', | 166 | title: '网络异常', |
168 | content: '请检查您的网络连接是否正常!', | 167 | content: '请检查您的网络连接是否正常!', |
169 | }); | 168 | }); |
170 | - return; | ||
171 | } | 169 | } |
172 | } catch (error) { | 170 | } catch (error) { |
173 | throw new Error(error); | 171 | throw new Error(error); |
174 | } | 172 | } |
175 | checkStatus(error.response && error.response.status, msg); | 173 | checkStatus(error.response && error.response.status, msg); |
176 | - return error; | 174 | + return Promise.reject(error); |
177 | }, | 175 | }, |
178 | }; | 176 | }; |
179 | 177 |
src/utils/http/axios/types.ts
src/views/demo/comp/upload/index.vue
1 | <template> | 1 | <template> |
2 | <div class="p-4"> | 2 | <div class="p-4"> |
3 | - <UploadContainer :maxSize="5" /> | 3 | + <a-alert message="基础示例" class="my-5"></a-alert> |
4 | + <BasicUpload :maxSize="20" :maxNumber="10" @change="handleChange" :api="uploadApi" /> | ||
5 | + | ||
6 | + <a-alert message="嵌入表单,加入表单校验" class="my-5"></a-alert> | ||
7 | + | ||
8 | + <BasicForm @register="register" /> | ||
4 | </div> | 9 | </div> |
5 | </template> | 10 | </template> |
6 | <script lang="ts"> | 11 | <script lang="ts"> |
7 | - import { defineComponent } from 'vue'; | ||
8 | - import { UploadContainer } from '/@/components/Upload/index'; | 12 | + import { defineComponent, h } from 'vue'; |
13 | + import { BasicUpload } from '/@/components/Upload'; | ||
14 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
15 | + import { BasicForm, FormSchema, useForm } from '/@/components/Form/index'; | ||
9 | 16 | ||
17 | + import { uploadApi } from '/@/api/sys/upload'; | ||
10 | // import { Alert } from 'ant-design-vue'; | 18 | // import { Alert } from 'ant-design-vue'; |
19 | + | ||
20 | + const schemas: FormSchema[] = [ | ||
21 | + { | ||
22 | + field: 'field1', | ||
23 | + component: 'Input', | ||
24 | + label: '字段1', | ||
25 | + colProps: { | ||
26 | + span: 8, | ||
27 | + }, | ||
28 | + rules: [{ required: true, type: 'array', message: '请选择上传文件' }], | ||
29 | + render: ({ model, field }) => { | ||
30 | + return h(BasicUpload, { | ||
31 | + value: model[field], | ||
32 | + api: uploadApi, | ||
33 | + onChange: (val: string[]) => { | ||
34 | + model[field] = val; | ||
35 | + }, | ||
36 | + }); | ||
37 | + }, | ||
38 | + }, | ||
39 | + ]; | ||
11 | export default defineComponent({ | 40 | export default defineComponent({ |
12 | - components: { UploadContainer }, | 41 | + components: { BasicUpload, BasicForm }, |
13 | setup() { | 42 | setup() { |
14 | - return {}; | 43 | + const { createMessage } = useMessage(); |
44 | + const [register] = useForm({ | ||
45 | + labelWidth: 120, | ||
46 | + schemas, | ||
47 | + actionColOptions: { | ||
48 | + span: 16, | ||
49 | + }, | ||
50 | + }); | ||
51 | + return { | ||
52 | + handleChange: (list: string[]) => { | ||
53 | + createMessage.info(`已上传文件${JSON.stringify(list)}`); | ||
54 | + }, | ||
55 | + uploadApi, | ||
56 | + register, | ||
57 | + }; | ||
15 | }, | 58 | }, |
16 | }); | 59 | }); |
17 | </script> | 60 | </script> |
src/views/demo/feat/download/index.vue
@@ -8,11 +8,21 @@ | @@ -8,11 +8,21 @@ | ||
8 | 8 | ||
9 | <a-alert message="base64流下载" /> | 9 | <a-alert message="base64流下载" /> |
10 | <a-button type="primary" class="my-4" @click="handleDownloadByBase64"> base64流下载 </a-button> | 10 | <a-button type="primary" class="my-4" @click="handleDownloadByBase64"> base64流下载 </a-button> |
11 | + | ||
12 | + <a-alert message="图片Url下载,如果有跨域问题,需要处理图片跨域" /> | ||
13 | + <a-button type="primary" class="my-4" @click="handleDownloadByOnlineUrl"> | ||
14 | + 图片Url下载 | ||
15 | + </a-button> | ||
11 | </div> | 16 | </div> |
12 | </template> | 17 | </template> |
13 | <script lang="ts"> | 18 | <script lang="ts"> |
14 | import { defineComponent } from 'vue'; | 19 | import { defineComponent } from 'vue'; |
15 | - import { downloadByUrl, downloadByData, downloadByBase64 } from '/@/utils/file/FileDownload'; | 20 | + import { |
21 | + downloadByUrl, | ||
22 | + downloadByData, | ||
23 | + downloadByBase64, | ||
24 | + downloadByOnlineUrl, | ||
25 | + } from '/@/utils/file/download'; | ||
16 | import imgBase64 from './imgBase64'; | 26 | import imgBase64 from './imgBase64'; |
17 | export default defineComponent({ | 27 | export default defineComponent({ |
18 | setup() { | 28 | setup() { |
@@ -24,15 +34,28 @@ | @@ -24,15 +34,28 @@ | ||
24 | url: 'https://codeload.github.com/anncwb/vue-vben-admin-doc/zip/master', | 34 | url: 'https://codeload.github.com/anncwb/vue-vben-admin-doc/zip/master', |
25 | target: '_self', | 35 | target: '_self', |
26 | }); | 36 | }); |
37 | + | ||
38 | + downloadByUrl({ | ||
39 | + url: 'https://vebn.oss-cn-beijing.aliyuncs.com/vben/logo.png', | ||
40 | + target: '_self', | ||
41 | + }); | ||
27 | } | 42 | } |
28 | 43 | ||
29 | function handleDownloadByBase64() { | 44 | function handleDownloadByBase64() { |
30 | downloadByBase64(imgBase64, 'logo.png'); | 45 | downloadByBase64(imgBase64, 'logo.png'); |
31 | } | 46 | } |
47 | + | ||
48 | + function handleDownloadByOnlineUrl() { | ||
49 | + downloadByOnlineUrl( | ||
50 | + 'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5944817f47b8408e9f1442ece49d68ca~tplv-k3u1fbpfcp-watermark.image', | ||
51 | + 'logo.png' | ||
52 | + ); | ||
53 | + } | ||
32 | return { | 54 | return { |
33 | handleDownloadByUrl, | 55 | handleDownloadByUrl, |
34 | handleDownByData, | 56 | handleDownByData, |
35 | handleDownloadByBase64, | 57 | handleDownloadByBase64, |
58 | + handleDownloadByOnlineUrl, | ||
36 | }; | 59 | }; |
37 | }, | 60 | }, |
38 | }); | 61 | }); |
yarn.lock
@@ -1050,10 +1050,10 @@ | @@ -1050,10 +1050,10 @@ | ||
1050 | resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b" | 1050 | resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b" |
1051 | integrity sha512-BybEHU5/I9EQ0CcwKAqmreZ2bMnAXrqLCTptAc6vPetHMbrXdZfejP5mt57e/8PNSt/qE7BHniU5PCYA+PGIHw== | 1051 | integrity sha512-BybEHU5/I9EQ0CcwKAqmreZ2bMnAXrqLCTptAc6vPetHMbrXdZfejP5mt57e/8PNSt/qE7BHniU5PCYA+PGIHw== |
1052 | 1052 | ||
1053 | -"@iconify/json@^1.1.254": | ||
1054 | - version "1.1.256" | ||
1055 | - resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.256.tgz#0f138d421ab12faca2fdd49aaf4fbc0122db08e3" | ||
1056 | - integrity sha512-CeLKbKL3lvq8afhR3LEyaBqXZDC52fgU0Ij3LbTRCwPUsumLNzhXA7MzN/f0JDYfXm9LShkfpgMcm00wQaANgg== | 1053 | +"@iconify/json@^1.1.258": |
1054 | + version "1.1.258" | ||
1055 | + resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.258.tgz#392064ae8fd4c6d542c21bb4d0d57d5860f38abb" | ||
1056 | + integrity sha512-x5DKhRrg8v1NWmClWa8zA80gWQ9xevivsUAF4s8CyAl/ZplBsEE1funKuuVcIKjexyE1UXb7uFWrUKt1fB5n1A== | ||
1057 | 1057 | ||
1058 | "@koa/cors@^3.1.0": | 1058 | "@koa/cors@^3.1.0": |
1059 | version "3.1.0" | 1059 | version "3.1.0" |
@@ -1316,10 +1316,10 @@ | @@ -1316,10 +1316,10 @@ | ||
1316 | "@types/qs" "*" | 1316 | "@types/qs" "*" |
1317 | "@types/serve-static" "*" | 1317 | "@types/serve-static" "*" |
1318 | 1318 | ||
1319 | -"@types/fs-extra@^9.0.2": | ||
1320 | - version "9.0.3" | ||
1321 | - resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.3.tgz#9996e5cce993508c32325380b429f04a1327523e" | ||
1322 | - integrity sha512-NKdGoXLTFTRED3ENcfCsH8+ekV4gbsysanx2OPbstXVV6fZMgUCqTxubs6I9r7pbOJbFgVq1rpFtLURjKCZWUw== | 1319 | +"@types/fs-extra@^9.0.4": |
1320 | + version "9.0.4" | ||
1321 | + resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.4.tgz#12553138cf0438db9a31cdc8b0a3aa9332eb67aa" | ||
1322 | + integrity sha512-50GO5ez44lxK5MDH90DYHFFfqxH7+fTqEEnvguQRzJ/tY9qFrMSHLiYHite+F3SNmf7+LHC1eMXojuD+E3Qcyg== | ||
1323 | dependencies: | 1323 | dependencies: |
1324 | "@types/node" "*" | 1324 | "@types/node" "*" |
1325 | 1325 | ||
@@ -1725,18 +1725,18 @@ | @@ -1725,18 +1725,18 @@ | ||
1725 | vscode-languageserver-textdocument "^1.0.1" | 1725 | vscode-languageserver-textdocument "^1.0.1" |
1726 | vscode-uri "^2.1.2" | 1726 | vscode-uri "^2.1.2" |
1727 | 1727 | ||
1728 | -"@vueuse/core@^4.0.0-beta.40": | ||
1729 | - version "4.0.0-beta.40" | ||
1730 | - resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-beta.40.tgz#7efdc15c1b994647dff7ae65c0ca573d96ce9b28" | ||
1731 | - integrity sha512-FOTOUrXAAp0NOmy8hMlP1HpUhnB8LeRJZDOEUl/A9gKMDwWvPTEvxKsDAIzSa4s7I0MapVzfeP3soNCNfl9+vQ== | 1728 | +"@vueuse/core@^4.0.0-beta.41": |
1729 | + version "4.0.0-beta.41" | ||
1730 | + resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-beta.41.tgz#0058aed5ade75ae2866283498009ad5172cbae84" | ||
1731 | + integrity sha512-CgUih65PzYScorm1S4F93e6XXm+qxA8GrRLOSB1kXaqtP6vXedwkBxKkNEYNACx4reL4VEHqM/BrM6FajXkQUg== | ||
1732 | dependencies: | 1732 | dependencies: |
1733 | - "@vueuse/shared" "4.0.0-beta.40" | 1733 | + "@vueuse/shared" "4.0.0-beta.41" |
1734 | vue-demi latest | 1734 | vue-demi latest |
1735 | 1735 | ||
1736 | -"@vueuse/shared@4.0.0-beta.40": | ||
1737 | - version "4.0.0-beta.40" | ||
1738 | - resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-beta.40.tgz#76e9b52228159e7ec88df2c8f4eea8fce1a42ec3" | ||
1739 | - integrity sha512-Ay71viUTXs0XX2hQ04kEExhpsCrw3KankBMP7euorsPjuQmIZjUA4NNOb45UAudg+uF5HXLpgWLvwb4cMOLHnQ== | 1736 | +"@vueuse/shared@4.0.0-beta.41": |
1737 | + version "4.0.0-beta.41" | ||
1738 | + resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-beta.41.tgz#395782ea2e580f1fc9488d25c89bd09f70170b25" | ||
1739 | + integrity sha512-dqnuEPPC3OUJ6L6rhMiOCuPWIR698DtdwOydwCZBISsG2V6gZ2QFND6xtRwLib6/lhUMYVYPwIz3hPjlx7BIzw== | ||
1740 | dependencies: | 1740 | dependencies: |
1741 | vue-demi latest | 1741 | vue-demi latest |
1742 | 1742 | ||
@@ -1850,10 +1850,10 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: | @@ -1850,10 +1850,10 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: | ||
1850 | dependencies: | 1850 | dependencies: |
1851 | color-convert "^2.0.1" | 1851 | color-convert "^2.0.1" |
1852 | 1852 | ||
1853 | -ant-design-vue@^2.0.0-beta.15: | ||
1854 | - version "2.0.0-beta.15" | ||
1855 | - resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.0-beta.15.tgz#3c787dabb70a33885d0e751e58f9a5610ed06134" | ||
1856 | - integrity sha512-OxZy+ZYU3LauIL4Rhqwy441K/iD++Cit6upnQy5+LVUrX0PSObPqPqMWVpncbAmJJYTEz88gkvgGeYqBdzouWA== | 1853 | +ant-design-vue@^2.0.0-rc.1: |
1854 | + version "2.0.0-rc.1" | ||
1855 | + resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.0-rc.1.tgz#2ef02475f3aa4c1474f2fe3cf44a52c34787be02" | ||
1856 | + integrity sha512-iKXkFtTHarvLHV7LWmYh6g/Cmkv+xK+vS621A1Qvg37Z6lCGg3K9BGAizmklAYzOTiPz0Ltt63eSiNqYMGh52g== | ||
1857 | dependencies: | 1857 | dependencies: |
1858 | "@ant-design-vue/use" "^0.0.1-0" | 1858 | "@ant-design-vue/use" "^0.0.1-0" |
1859 | "@ant-design/icons-vue" "^5.1.5" | 1859 | "@ant-design/icons-vue" "^5.1.5" |
@@ -8109,10 +8109,10 @@ vary@^1.1.2: | @@ -8109,10 +8109,10 @@ vary@^1.1.2: | ||
8109 | resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" | 8109 | resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" |
8110 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= | 8110 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= |
8111 | 8111 | ||
8112 | -vditor@^3.6.0: | ||
8113 | - version "3.6.1" | ||
8114 | - resolved "https://registry.npmjs.org/vditor/-/vditor-3.6.1.tgz#b0b510f23d0cf0e5d8b3d36924e40400de96f692" | ||
8115 | - integrity sha512-83GdGLIWrV1x04aK8DO9aZidqQfmuGXXUbxSCuQxRla+T9KfnFRmJwfqIxXQm8h+4jUBCFL38e8PqLa3fBOf9w== | 8112 | +vditor@^3.6.2: |
8113 | + version "3.6.2" | ||
8114 | + resolved "https://registry.npmjs.org/vditor/-/vditor-3.6.2.tgz#ee6011efa3ec563c6356ed82efbf2e00ba2e35c6" | ||
8115 | + integrity sha512-HPHHun5+IXmYGMKDWcUD83VfP1Qfncz7DmaIKoWpluJgE8ve7s+4RbFBcaEpYPXYzIuL2UTHoMnIjmTPbenOCA== | ||
8116 | dependencies: | 8116 | dependencies: |
8117 | diff-match-patch "^1.0.5" | 8117 | diff-match-patch "^1.0.5" |
8118 | 8118 | ||
@@ -8272,10 +8272,10 @@ vue-i18n@^9.0.0-beta.6: | @@ -8272,10 +8272,10 @@ vue-i18n@^9.0.0-beta.6: | ||
8272 | dependencies: | 8272 | dependencies: |
8273 | source-map "^0.6.1" | 8273 | source-map "^0.6.1" |
8274 | 8274 | ||
8275 | -vue-router@^4.0.0-rc.2: | ||
8276 | - version "4.0.0-rc.2" | ||
8277 | - resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-rc.2.tgz#8545cab76a05ca4f6dffbe6c6a671a4dbf585ab2" | ||
8278 | - integrity sha512-51mBp39rzBFpk1nyU9SkhPcwR67gBzWIH8p3pyeDmtNYgWzGF3q8MneD/xbMwsfTQkw2H1qBk6uwRaVy3M8Nxw== | 8275 | +vue-router@^4.0.0-rc.3: |
8276 | + version "4.0.0-rc.3" | ||
8277 | + resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-rc.3.tgz#70d18e90030bc6a25e81a30401d673223998ec6b" | ||
8278 | + integrity sha512-NnPqWIfanEhJC4wu8BEFBmnEDIrx9ST0/HtmBiE+oV2MQlhyRk1TmdttWwVqx6Sh7kONsrI10GQV9l3YEkcWXg== | ||
8279 | 8279 | ||
8280 | vue-types@^3.0.0: | 8280 | vue-types@^3.0.0: |
8281 | version "3.0.1" | 8281 | version "3.0.1" |