mirror of
https://github.com/vbenjs/vben-admin-thin-next.git
synced 2025-01-23 17:50:22 +08:00
perf(upload): improve upload component
This commit is contained in:
parent
a161bfa818
commit
661db0c767
@ -3,6 +3,11 @@
|
||||
### ✨ Features
|
||||
|
||||
- 新增 base64 文件流下载
|
||||
- 优化上传组件及示例
|
||||
|
||||
### 🎫 Chores
|
||||
|
||||
- 更新 antdv 到`2.0.0-rc.1`
|
||||
|
||||
## 2.0.0-rc.10 (2020-11-13)
|
||||
|
||||
|
@ -226,10 +226,10 @@ yarn clean:lib # Delete node_modules, supported window
|
||||
- [x] Data import and export
|
||||
- [x] Global error handling
|
||||
- [x] Rich text component
|
||||
- [x] Upload component
|
||||
|
||||
## Developing features
|
||||
|
||||
- [ ] Upload component
|
||||
- [ ] Theme configuration
|
||||
- [ ] Dark theme
|
||||
- [ ] Build CDN
|
||||
|
@ -228,10 +228,10 @@ yarn clean:lib # 删除node_modules,兼容window系统
|
||||
- [x] 系统性能优化
|
||||
- [x] 全局错误处理
|
||||
- [x] 富文本组件
|
||||
- [x] 上传组件
|
||||
|
||||
## 正在开发的功能
|
||||
|
||||
- [ ] 上传组件
|
||||
- [ ] 主题配置
|
||||
- [ ] 黑暗主题
|
||||
- [ ] 打包 CDN
|
||||
|
12
package.json
12
package.json
@ -22,8 +22,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/iconify": "^2.0.0-rc.2",
|
||||
"@vueuse/core": "^4.0.0-beta.40",
|
||||
"ant-design-vue": "^2.0.0-beta.15",
|
||||
"@vueuse/core": "^4.0.0-beta.41",
|
||||
"ant-design-vue": "^2.0.0-rc.1",
|
||||
"apexcharts": "3.22.0",
|
||||
"axios": "^0.21.0",
|
||||
"echarts": "^4.9.0",
|
||||
@ -33,10 +33,10 @@
|
||||
"nprogress": "^0.2.0",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"qrcode": "^1.4.4",
|
||||
"vditor": "^3.6.0",
|
||||
"vditor": "^3.6.2",
|
||||
"vue": "^3.0.2",
|
||||
"vue-i18n": "^9.0.0-beta.6",
|
||||
"vue-router": "^4.0.0-rc.2",
|
||||
"vue-router": "^4.0.0-rc.3",
|
||||
"vuex": "^4.0.0-rc.1",
|
||||
"vuex-module-decorators": "^1.0.1",
|
||||
"xlsx": "^0.16.8",
|
||||
@ -45,11 +45,11 @@
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^11.0.0",
|
||||
"@commitlint/config-conventional": "^11.0.0",
|
||||
"@iconify/json": "^1.1.254",
|
||||
"@iconify/json": "^1.1.258",
|
||||
"@ls-lint/ls-lint": "^1.9.2",
|
||||
"@purge-icons/generated": "^0.4.1",
|
||||
"@types/echarts": "^4.9.0",
|
||||
"@types/fs-extra": "^9.0.2",
|
||||
"@types/fs-extra": "^9.0.4",
|
||||
"@types/koa-static": "^4.0.1",
|
||||
"@types/lodash-es": "^4.17.3",
|
||||
"@types/mockjs": "^1.0.3",
|
||||
|
@ -24,7 +24,7 @@ export const footerProps = {
|
||||
okButtonProps: Object as PropType<any>,
|
||||
okText: {
|
||||
type: String as PropType<string>,
|
||||
default: '保存',
|
||||
default: '确认',
|
||||
},
|
||||
okType: {
|
||||
type: String as PropType<string>,
|
||||
|
@ -44,7 +44,6 @@
|
||||
import { useFormValues } from './hooks/useFormValues';
|
||||
import useAdvanced from './hooks/useAdvanced';
|
||||
import { useFormAction } from './hooks/useFormAction';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicForm',
|
||||
components: { FormItem, Form, Row, FormAction },
|
||||
|
@ -18,7 +18,7 @@ export default defineComponent({
|
||||
// icon size
|
||||
size: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: 14,
|
||||
default: 16,
|
||||
},
|
||||
prefix: {
|
||||
type: String as PropType<string>,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { PropType } from 'vue';
|
||||
import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
||||
export const modalProps = {
|
||||
visible: Boolean as PropType<boolean>,
|
||||
// open drag
|
||||
@ -16,7 +17,7 @@ export const modalProps = {
|
||||
},
|
||||
okText: {
|
||||
type: String as PropType<string>,
|
||||
default: '保存',
|
||||
default: '确认',
|
||||
},
|
||||
closeFunc: Function as PropType<() => Promise<boolean>>,
|
||||
};
|
||||
@ -100,9 +101,9 @@ export const basicProps = Object.assign({}, modalProps, {
|
||||
default: 'primary',
|
||||
},
|
||||
|
||||
okButtonProps: Object as PropType<any>,
|
||||
okButtonProps: Object as PropType<ButtonProps>,
|
||||
|
||||
cancelButtonProps: Object as PropType<any>,
|
||||
cancelButtonProps: Object as PropType<ButtonProps>,
|
||||
|
||||
title: {
|
||||
type: String as PropType<string>,
|
||||
|
@ -7,7 +7,7 @@
|
||||
import { defineComponent, watchEffect, PropType, ref, unref } from 'vue';
|
||||
import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus';
|
||||
import { toDataURL } from 'qrcode';
|
||||
import { downloadByUrl } from '/@/utils/file/FileDownload';
|
||||
import { downloadByUrl } from '/@/utils/file/download';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'QrCode',
|
||||
|
@ -4,6 +4,7 @@
|
||||
class="basic-table"
|
||||
:class="{
|
||||
'table-form-container': getBindValues.useSearchForm,
|
||||
inset: getBindValues.inset,
|
||||
}"
|
||||
>
|
||||
<BasicForm
|
||||
|
@ -84,7 +84,7 @@ export function useDataSource(
|
||||
const { api, searchInfo, fetchSetting, beforeFetch, afterFetch, useSearchForm } = unref(
|
||||
propsRef
|
||||
);
|
||||
if (!api && !isFunction(api)) return;
|
||||
if (!api || !isFunction(api)) return;
|
||||
try {
|
||||
loadingRef.value = true;
|
||||
const { pageField, sizeField, listField, totalField } = fetchSetting || FETCH_SETTING;
|
||||
|
@ -16,7 +16,10 @@ export const basicProps = {
|
||||
tableSetting: {
|
||||
type: Object as PropType<TableSetting>,
|
||||
},
|
||||
|
||||
inset: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
sortFn: {
|
||||
type: Function as PropType<(sortInfo: SorterResult) => any>,
|
||||
default: DEFAULT_SORT_FN,
|
||||
|
@ -49,6 +49,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.inset {
|
||||
.ant-table-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
.ant-table {
|
||||
border: none;
|
||||
|
@ -126,6 +126,8 @@ export interface TableSetting {
|
||||
export interface BasicTableProps<T = any> {
|
||||
// 自定义排序方法
|
||||
sortFn?: (sortInfo: SorterResult) => any;
|
||||
// 取消表格的默认padding
|
||||
inset?: boolean;
|
||||
// 显示表格设置
|
||||
showTableSetting?: boolean;
|
||||
tableSetting?: TableSetting;
|
||||
|
@ -1,2 +1,2 @@
|
||||
export { default as UploadContainer } from './src/UploadContainer.vue';
|
||||
export { default as BasicUpload } from './src/BasicUpload.vue';
|
||||
// export * from './src/types';
|
||||
|
99
src/components/Upload/src/BasicUpload.vue
Normal file
99
src/components/Upload/src/BasicUpload.vue
Normal file
@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-button-group>
|
||||
<a-button type="primary" @click="openUploadModal" preIcon="ant-design:cloud-upload-outlined">
|
||||
上传
|
||||
</a-button>
|
||||
<Tooltip placement="bottom" v-if="showPreview">
|
||||
<template #title>
|
||||
已上传
|
||||
<template v-if="fileListRef.length">{{ fileListRef.length }}</template>
|
||||
</template>
|
||||
<a-button @click="openPreviewModal">
|
||||
<Icon icon="ant-design:eye-outlined" />
|
||||
<template v-if="fileListRef.length && showPreviewNumber">
|
||||
{{ fileListRef.length }}
|
||||
</template>
|
||||
</a-button>
|
||||
</Tooltip>
|
||||
</a-button-group>
|
||||
|
||||
<UploadModal v-bind="bindValue" @register="registerUploadModal" @change="handleChange" />
|
||||
|
||||
<UploadPreviewModal
|
||||
:value="fileListRef"
|
||||
@register="registerPreviewModal"
|
||||
@list-change="handlePreviewChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch, unref, computed } from 'vue';
|
||||
|
||||
import UploadModal from './UploadModal.vue';
|
||||
import UploadPreviewModal from './UploadPreviewModal.vue';
|
||||
import Icon from '/@/components/Icon';
|
||||
import { Tooltip } from 'ant-design-vue';
|
||||
|
||||
import { useModal } from '/@/components/Modal';
|
||||
|
||||
import { uploadContainerProps } from './props';
|
||||
import { omit } from 'lodash-es';
|
||||
|
||||
export default defineComponent({
|
||||
components: { UploadModal, UploadPreviewModal, Icon, Tooltip },
|
||||
props: uploadContainerProps,
|
||||
setup(props, { emit, attrs }) {
|
||||
// 上传modal
|
||||
const [registerUploadModal, { openModal: openUploadModal }] = useModal();
|
||||
|
||||
// 预览modal
|
||||
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal();
|
||||
|
||||
const fileListRef = ref<string[]>([]);
|
||||
|
||||
const showPreview = computed(() => {
|
||||
const { emptyHidePreview } = props;
|
||||
if (!emptyHidePreview) return true;
|
||||
return emptyHidePreview ? fileListRef.value.length > 0 : true;
|
||||
});
|
||||
|
||||
const bindValue = computed(() => {
|
||||
const value = { ...attrs, ...props };
|
||||
return omit(value, 'onChange');
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(value = []) => {
|
||||
fileListRef.value = value;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 上传modal保存操作
|
||||
function handleChange(urls: string[]) {
|
||||
fileListRef.value = [...unref(fileListRef), ...(urls || [])];
|
||||
emit('change', fileListRef.value);
|
||||
}
|
||||
|
||||
// 预览modal保存操作
|
||||
function handlePreviewChange(urls: string[]) {
|
||||
fileListRef.value = [...(urls || [])];
|
||||
emit('change', fileListRef.value);
|
||||
}
|
||||
|
||||
return {
|
||||
registerUploadModal,
|
||||
openUploadModal,
|
||||
handleChange,
|
||||
handlePreviewChange,
|
||||
registerPreviewModal,
|
||||
openPreviewModal,
|
||||
fileListRef,
|
||||
showPreview,
|
||||
bindValue,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
@ -5,25 +5,22 @@
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
fileUrl: {
|
||||
type: String,
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
fileType: {
|
||||
type: String,
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
fileName: {
|
||||
type: String,
|
||||
type: String as PropType<string>,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,62 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-button-group>
|
||||
<a-button type="primary" @click="openUploadModal">上传</a-button>
|
||||
<a-button @click="openPreviewModal">
|
||||
<Icon icon="ant-design:eye-outlined" />
|
||||
</a-button>
|
||||
</a-button-group>
|
||||
<UploadModal v-bind="$props" @register="registerUploadModal" @change="handleChange" />
|
||||
<UploadPreviewModal
|
||||
:value="fileListRef"
|
||||
@register="registerPreviewModal"
|
||||
@change="handlePreviewChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch, unref } from 'vue';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import UploadModal from './UploadModal.vue';
|
||||
import { uploadContainerProps } from './props';
|
||||
import UploadPreviewModal from './UploadPreviewModal.vue';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
export default defineComponent({
|
||||
components: { UploadModal, UploadPreviewModal, Icon },
|
||||
props: uploadContainerProps,
|
||||
setup(props, { emit }) {
|
||||
// 上传modal
|
||||
const [registerUploadModal, { openModal: openUploadModal }] = useModal();
|
||||
// 预览modal
|
||||
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal();
|
||||
|
||||
const fileListRef = ref<string[]>([]);
|
||||
watch(
|
||||
() => props.value,
|
||||
(value) => {
|
||||
fileListRef.value = [...(value || [])];
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
// 上传modal保存操作
|
||||
function handleChange(urls: string[]) {
|
||||
fileListRef.value = [...unref(fileListRef), ...(urls || [])];
|
||||
emit('change', fileListRef.value);
|
||||
}
|
||||
// 预览modal保存操作
|
||||
function handlePreviewChange(urls: string[]) {
|
||||
fileListRef.value = [...(urls || [])];
|
||||
emit('change', fileListRef.value);
|
||||
}
|
||||
return {
|
||||
registerUploadModal,
|
||||
openUploadModal,
|
||||
handleChange,
|
||||
handlePreviewChange,
|
||||
registerPreviewModal,
|
||||
openPreviewModal,
|
||||
fileListRef,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,31 +1,44 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
width="800px"
|
||||
title="上传"
|
||||
okText="保存"
|
||||
v-bind="$attrs"
|
||||
@register="register"
|
||||
@ok="handleOk"
|
||||
:closeFunc="handleCloseFunc"
|
||||
:maskClosable="false"
|
||||
width="800px"
|
||||
title="上传组件"
|
||||
:keyboard="false"
|
||||
wrapClassName="upload-modal"
|
||||
:okButtonProps="{ disabled: isUploadingRef }"
|
||||
:okButtonProps="getOkButtonProps"
|
||||
:cancelButtonProps="{ disabled: isUploadingRef }"
|
||||
>
|
||||
<template #centerdFooter>
|
||||
<a-button @click="handleStartUpload" color="success" :loading="isUploadingRef">
|
||||
{{ isUploadingRef ? '上传中' : '开始上传' }}
|
||||
<a-button
|
||||
@click="handleStartUpload"
|
||||
color="success"
|
||||
:disabled="!getIsSelectFile"
|
||||
:loading="isUploadingRef"
|
||||
>
|
||||
{{ getUploadBtnText }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<BasicTable @register="registerTable" :dataSource="fileListRef">
|
||||
<template #toolbar>
|
||||
<Upload :accept="getStringAccept" :multiple="multiple" :before-upload="beforeUpload">
|
||||
<a-button type="primary"> 选择文件 </a-button>
|
||||
<span class="px-2">{{ getHelpText }}</span>
|
||||
</Upload>
|
||||
<BasicTable @register="registerTable" :dataSource="fileListRef" />
|
||||
</template>
|
||||
<template #tableTitle>
|
||||
<Alert :message="getHelpText" type="info" banner></Alert>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, ref, toRef, unref } from 'vue';
|
||||
import { Upload } from 'ant-design-vue';
|
||||
import { defineComponent, reactive, ref, toRefs, unref, computed } from 'vue';
|
||||
import { Upload, Alert } from 'ant-design-vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable } from '/@/components/Table';
|
||||
// hooks
|
||||
@ -39,23 +52,56 @@
|
||||
import { checkFileType, checkImgType, getBase64WithFile } from './utils';
|
||||
import { buildUUID } from '/@/utils/uuid';
|
||||
import { createImgPreview } from '/@/components/Preview/index';
|
||||
import { uploadApi } from '/@/api/demo/upload';
|
||||
import { uploadApi } from '/@/api/sys/upload';
|
||||
import { isFunction } from '/@/utils/is';
|
||||
import { warn } from '/@/utils/log';
|
||||
|
||||
export default defineComponent({
|
||||
components: { BasicModal, Upload, BasicTable },
|
||||
components: { BasicModal, Upload, BasicTable, Alert },
|
||||
props: basicProps,
|
||||
setup(props, { emit }) {
|
||||
const [register, { closeModal }] = useModalInner();
|
||||
const { getAccept, getStringAccept, getHelpText } = useUploadType({
|
||||
acceptRef: toRef(props, 'accept'),
|
||||
helpTextRef: toRef(props, 'helpText'),
|
||||
maxNumberRef: toRef(props, 'maxNumber'),
|
||||
maxSizeRef: toRef(props, 'maxSize'),
|
||||
// 是否正在上传
|
||||
const isUploadingRef = ref(false);
|
||||
const fileListRef = ref<FileItem[]>([]);
|
||||
const state = reactive<{ fileList: FileItem[] }>({
|
||||
fileList: [],
|
||||
});
|
||||
|
||||
const [register, { closeModal }] = useModalInner();
|
||||
|
||||
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
|
||||
const { getAccept, getStringAccept, getHelpText } = useUploadType({
|
||||
acceptRef: accept,
|
||||
helpTextRef: helpText,
|
||||
maxNumberRef: maxNumber,
|
||||
maxSizeRef: maxSize,
|
||||
});
|
||||
|
||||
const fileListRef = ref<FileItem[]>([]);
|
||||
const state = reactive<{ fileList: FileItem[] }>({ fileList: [] });
|
||||
const { createMessage } = useMessage();
|
||||
|
||||
const getIsSelectFile = computed(() => {
|
||||
return (
|
||||
fileListRef.value.length > 0 &&
|
||||
!fileListRef.value.every((item) => item.status === UploadResultStatus.SUCCESS)
|
||||
);
|
||||
});
|
||||
|
||||
const getOkButtonProps = computed(() => {
|
||||
const someSuccess = fileListRef.value.some(
|
||||
(item) => item.status === UploadResultStatus.SUCCESS
|
||||
);
|
||||
return {
|
||||
disabled: isUploadingRef.value || fileListRef.value.length === 0 || !someSuccess,
|
||||
};
|
||||
});
|
||||
|
||||
const getUploadBtnText = computed(() => {
|
||||
const someError = fileListRef.value.some(
|
||||
(item) => item.status === UploadResultStatus.ERROR
|
||||
);
|
||||
return isUploadingRef.value ? '上传中' : someError ? '重新上传失败文件' : '开始上传';
|
||||
});
|
||||
|
||||
// 上传前校验
|
||||
function beforeUpload(file: File) {
|
||||
const { size, name } = file;
|
||||
@ -73,6 +119,14 @@
|
||||
createMessage.error!(`只能上传${accept.join(',')}格式文件`);
|
||||
return false;
|
||||
}
|
||||
const commonItem = {
|
||||
uuid: buildUUID(),
|
||||
file,
|
||||
size,
|
||||
name,
|
||||
percent: 0,
|
||||
type: name.split('.').pop(),
|
||||
};
|
||||
// 生成图片缩略图
|
||||
if (checkImgType(file)) {
|
||||
// beforeUpload,如果异步会调用自带上传方法
|
||||
@ -81,29 +135,13 @@
|
||||
fileListRef.value = [
|
||||
...unref(fileListRef),
|
||||
{
|
||||
uuid: buildUUID(),
|
||||
file,
|
||||
thumbUrl,
|
||||
size,
|
||||
name,
|
||||
percent: 0,
|
||||
type: name.split('.').pop(),
|
||||
...commonItem,
|
||||
},
|
||||
];
|
||||
});
|
||||
} else {
|
||||
fileListRef.value = [
|
||||
...unref(fileListRef),
|
||||
{
|
||||
uuid: buildUUID(),
|
||||
|
||||
file,
|
||||
size,
|
||||
name,
|
||||
percent: 0,
|
||||
type: name.split('.').pop(),
|
||||
},
|
||||
];
|
||||
fileListRef.value = [...unref(fileListRef), commonItem];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -112,6 +150,7 @@
|
||||
const index = fileListRef.value.findIndex((item) => item.uuid === record.uuid);
|
||||
index !== -1 && fileListRef.value.splice(index, 1);
|
||||
}
|
||||
|
||||
// 预览
|
||||
function handlePreview(record: FileItem) {
|
||||
const { thumbUrl = '' } = record;
|
||||
@ -119,19 +158,18 @@
|
||||
imageList: [thumbUrl],
|
||||
});
|
||||
}
|
||||
const [registerTable] = useTable({
|
||||
columns: createTableColumns(),
|
||||
actionColumn: createActionColumn(handleRemove, handlePreview),
|
||||
pagination: false,
|
||||
});
|
||||
// 是否正在上传
|
||||
const isUploadingRef = ref(false);
|
||||
|
||||
async function uploadApiByItem(item: FileItem) {
|
||||
const { api } = props;
|
||||
if (!api || !isFunction(api)) {
|
||||
return warn('upload api must exist and be a function');
|
||||
}
|
||||
try {
|
||||
item.status = UploadResultStatus.UPLOADING;
|
||||
|
||||
const { data } = await uploadApi(
|
||||
{
|
||||
...(props.uploadParams || {}),
|
||||
file: item.file,
|
||||
},
|
||||
function onUploadProgress(progressEvent: ProgressEvent) {
|
||||
@ -154,32 +192,42 @@
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 点击开始上传
|
||||
async function handleStartUpload() {
|
||||
const { maxNumber } = props;
|
||||
if (fileListRef.value.length > maxNumber) {
|
||||
return createMessage.warning(`最多只能上传${maxNumber}个文件`);
|
||||
}
|
||||
try {
|
||||
isUploadingRef.value = true;
|
||||
// 只上传不是成功状态的
|
||||
const uploadFileList =
|
||||
fileListRef.value.filter((item) => item.status !== UploadResultStatus.SUCCESS) || [];
|
||||
const data = await Promise.all(
|
||||
unref(fileListRef).map((item) => {
|
||||
uploadFileList.map((item) => {
|
||||
return uploadApiByItem(item);
|
||||
})
|
||||
);
|
||||
isUploadingRef.value = false;
|
||||
// 生产环境:抛出错误
|
||||
const errorList = data.filter((item) => !item.success);
|
||||
if (errorList.length > 0) {
|
||||
throw errorList;
|
||||
}
|
||||
const errorList = data.filter((item: any) => !item.success);
|
||||
if (errorList.length > 0) throw errorList;
|
||||
} catch (e) {
|
||||
isUploadingRef.value = false;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// 点击保存
|
||||
function handleOk() {
|
||||
// TODO: 没起作用:okButtonProps={{ disabled: state.isUploading }}
|
||||
const { maxNumber } = props;
|
||||
|
||||
if (fileListRef.value.length > maxNumber) {
|
||||
return createMessage.warning(`最多只能上传${maxNumber}个文件`);
|
||||
}
|
||||
if (isUploadingRef.value) {
|
||||
createMessage.warning('请等待文件上传后,保存');
|
||||
return;
|
||||
return createMessage.warning('请等待文件上传后,保存');
|
||||
}
|
||||
const fileList: string[] = [];
|
||||
|
||||
@ -189,18 +237,15 @@
|
||||
fileList.push(responseData.url);
|
||||
}
|
||||
}
|
||||
|
||||
// 存在一个上传成功的即可保存
|
||||
|
||||
if (fileList.length <= 0) {
|
||||
createMessage.warning('没有上传成功的文件,无法保存');
|
||||
return;
|
||||
return createMessage.warning('没有上传成功的文件,无法保存');
|
||||
}
|
||||
console.log(fileList);
|
||||
emit('change', fileList);
|
||||
fileListRef.value = [];
|
||||
closeModal();
|
||||
emit('change', fileList);
|
||||
}
|
||||
|
||||
// 点击关闭:则所有操作不保存,包括上传的
|
||||
function handleCloseFunc() {
|
||||
if (!isUploadingRef.value) {
|
||||
@ -211,11 +256,22 @@
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const [registerTable] = useTable({
|
||||
columns: createTableColumns(),
|
||||
actionColumn: createActionColumn(handleRemove, handlePreview),
|
||||
pagination: false,
|
||||
inset: true,
|
||||
scroll: {
|
||||
y: 3000,
|
||||
},
|
||||
});
|
||||
return {
|
||||
register,
|
||||
closeModal,
|
||||
getHelpText,
|
||||
getStringAccept,
|
||||
getOkButtonProps,
|
||||
beforeUpload,
|
||||
registerTable,
|
||||
fileListRef,
|
||||
@ -224,14 +280,13 @@
|
||||
handleStartUpload,
|
||||
handleOk,
|
||||
handleCloseFunc,
|
||||
getIsSelectFile,
|
||||
getUploadBtnText,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
// /deep/ .ant-upload-list {
|
||||
// display: none;
|
||||
// }
|
||||
.upload-modal {
|
||||
.ant-upload-list {
|
||||
display: none;
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
width="800px"
|
||||
title="预览"
|
||||
wrapClassName="upload-preview-modal"
|
||||
v-bind="$attrs"
|
||||
width="800px"
|
||||
@register="register"
|
||||
title="预览"
|
||||
:showOkBtn="false"
|
||||
>
|
||||
<BasicTable @register="registerTable" :dataSource="fileListRef" />
|
||||
@ -12,17 +12,18 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch, ref, unref } from 'vue';
|
||||
|
||||
import { BasicTable, useTable } from '/@/components/Table';
|
||||
import { createPreviewColumns, createPreviewActionColumn } from './data';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { priviewProps } from './props';
|
||||
import { previewProps } from './props';
|
||||
import { PreviewFileItem } from './types';
|
||||
import { createImgPreview } from '/@/components/Preview/index';
|
||||
import { downloadByUrl } from '/@/utils/file/FileDownload';
|
||||
import { downloadByUrl } from '/@/utils/file/download';
|
||||
|
||||
import { createPreviewColumns, createPreviewActionColumn } from './data';
|
||||
export default defineComponent({
|
||||
components: { BasicModal, BasicTable },
|
||||
props: priviewProps,
|
||||
props: previewProps,
|
||||
setup(props, { emit }) {
|
||||
const [register, { closeModal }] = useModalInner();
|
||||
const fileListRef = ref<PreviewFileItem[]>([]);
|
||||
@ -43,17 +44,19 @@
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 删除
|
||||
function handleRemove(record: PreviewFileItem) {
|
||||
const index = fileListRef.value.findIndex((item) => item.url === record.url);
|
||||
if (index !== -1) {
|
||||
fileListRef.value.splice(index, 1);
|
||||
emit(
|
||||
'change',
|
||||
'list-change',
|
||||
fileListRef.value.map((item) => item.url)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 预览
|
||||
function handlePreview(record: PreviewFileItem) {
|
||||
const { url = '' } = record;
|
||||
@ -61,16 +64,19 @@
|
||||
imageList: [url],
|
||||
});
|
||||
}
|
||||
|
||||
// 下载
|
||||
function handleDownload(record: PreviewFileItem) {
|
||||
const { url = '' } = record;
|
||||
downloadByUrl({ url });
|
||||
}
|
||||
|
||||
const [registerTable] = useTable({
|
||||
columns: createPreviewColumns(),
|
||||
pagination: false,
|
||||
actionColumn: createPreviewActionColumn({ handleRemove, handlePreview, handleDownload }),
|
||||
});
|
||||
|
||||
return {
|
||||
register,
|
||||
closeModal,
|
||||
|
@ -1,10 +1,6 @@
|
||||
// import { BasicColumn, TableAction, ActionItem } from '@/components/table';
|
||||
import { checkImgType, isImgTypeByName } from './utils';
|
||||
// import ThumnUrl from './ThumbUrl.vue';
|
||||
import { Progress } from 'ant-design-vue';
|
||||
import { Progress, Tag } from 'ant-design-vue';
|
||||
import { FileItem, PreviewFileItem, UploadResultStatus } from './types';
|
||||
// import { ElecArchivesSaveResult } from '@/api/biz/file/model/fileModel';
|
||||
// import { quryFile } from '@/api/biz/file/file';
|
||||
import { BasicColumn, ActionItem, TableAction } from '/@/components/Table/index';
|
||||
|
||||
// 文件上传列表
|
||||
@ -16,8 +12,7 @@ export function createTableColumns(): BasicColumn[] {
|
||||
width: 100,
|
||||
customRender: ({ record }) => {
|
||||
const { thumbUrl, type } = (record as FileItem) || {};
|
||||
return <span>{thumbUrl ? <img src={thumbUrl} style={{ width: '50px' }} /> : type}</span>;
|
||||
// return <ThumnUrl fileUrl={thumbUrl} fileType={type} fileName={type} />;
|
||||
return <span>{thumbUrl ? <img style={{ maxWidth: '60px' }} src={thumbUrl} /> : type}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -26,7 +21,7 @@ export function createTableColumns(): BasicColumn[] {
|
||||
align: 'left',
|
||||
customRender: ({ text, record }) => {
|
||||
const { percent, status: uploadStatus } = (record as FileItem) || {};
|
||||
let status = 'normal';
|
||||
let status: 'normal' | 'exception' | 'active' | 'success' = 'normal';
|
||||
if (uploadStatus === UploadResultStatus.ERROR) {
|
||||
status = 'exception';
|
||||
} else if (uploadStatus === UploadResultStatus.UPLOADING) {
|
||||
@ -63,11 +58,11 @@ export function createTableColumns(): BasicColumn[] {
|
||||
width: 100,
|
||||
customRender: ({ text }) => {
|
||||
if (text === UploadResultStatus.SUCCESS) {
|
||||
return '上传成功';
|
||||
return <Tag color="green">{() => '上传成功'}</Tag>;
|
||||
} else if (text === UploadResultStatus.ERROR) {
|
||||
return '上传失败';
|
||||
return <Tag color="red">{() => '上传失败'}</Tag>;
|
||||
} else if (text === UploadResultStatus.UPLOADING) {
|
||||
return '上传中';
|
||||
return <Tag color="blue">{() => '上传中'}</Tag>;
|
||||
}
|
||||
|
||||
return text;
|
||||
@ -85,6 +80,7 @@ export function createActionColumn(handleRemove: Function, handlePreview: Functi
|
||||
const actions: ActionItem[] = [
|
||||
{
|
||||
label: '删除',
|
||||
color: 'error',
|
||||
onClick: handleRemove.bind(null, record),
|
||||
},
|
||||
];
|
||||
@ -125,9 +121,9 @@ export function createPreviewActionColumn({
|
||||
handlePreview,
|
||||
handleDownload,
|
||||
}: {
|
||||
handleRemove: Function;
|
||||
handlePreview: Function;
|
||||
handleDownload: Function;
|
||||
handleRemove: Fn;
|
||||
handlePreview: Fn;
|
||||
handleDownload: Fn;
|
||||
}): BasicColumn {
|
||||
return {
|
||||
width: 160,
|
||||
@ -135,11 +131,12 @@ export function createPreviewActionColumn({
|
||||
dataIndex: 'action',
|
||||
fixed: false,
|
||||
customRender: ({ record }) => {
|
||||
const { url } = (record as PreviewFileItem) || {};
|
||||
const { url } = (record || {}) as PreviewFileItem;
|
||||
|
||||
const actions: ActionItem[] = [
|
||||
{
|
||||
label: '删除',
|
||||
color: 'error',
|
||||
onClick: handleRemove.bind(null, record),
|
||||
},
|
||||
{
|
||||
|
@ -10,10 +10,10 @@ export const basicProps = {
|
||||
type: Number as PropType<number>,
|
||||
default: 2,
|
||||
},
|
||||
// 最大数量的文件,0不限制
|
||||
// 最大数量的文件,Infinity不限制
|
||||
maxNumber: {
|
||||
type: Number as PropType<number>,
|
||||
default: 0,
|
||||
default: Infinity,
|
||||
},
|
||||
// 根据后缀,或者其他
|
||||
accept: {
|
||||
@ -21,9 +21,18 @@ export const basicProps = {
|
||||
default: () => [],
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true,
|
||||
},
|
||||
uploadParams: {
|
||||
type: Object as PropType<any>,
|
||||
default: {},
|
||||
},
|
||||
api: {
|
||||
type: Function as PropType<PromiseFn>,
|
||||
default: null,
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const uploadContainerProps = {
|
||||
@ -32,9 +41,17 @@ export const uploadContainerProps = {
|
||||
default: () => [],
|
||||
},
|
||||
...basicProps,
|
||||
showPreviewNumber: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true,
|
||||
},
|
||||
emptyHidePreview: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const priviewProps = {
|
||||
export const previewProps = {
|
||||
value: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { UploadApiResult } from '/@/api/demo/model/uploadModel';
|
||||
import { UploadApiResult } from '/@/api/sys/model/uploadModel';
|
||||
|
||||
export enum UploadResultStatus {
|
||||
SUCCESS = 'success',
|
||||
|
@ -42,12 +42,12 @@ export function useUploadType({
|
||||
|
||||
const maxSize = unref(maxSizeRef);
|
||||
if (maxSize) {
|
||||
helpTexts.push(`不超过${maxSize}MB`);
|
||||
helpTexts.push(`单个文件不超过${maxSize}MB`);
|
||||
}
|
||||
|
||||
const maxNumber = unref(maxNumberRef);
|
||||
if (maxNumber) {
|
||||
helpTexts.push(`最多可选择${maxNumber}个文件`);
|
||||
if (maxNumber && maxNumber !== Infinity) {
|
||||
helpTexts.push(`最多只能上传${maxNumber}个文件`);
|
||||
}
|
||||
return helpTexts.join(',');
|
||||
});
|
||||
|
@ -3,18 +3,17 @@ export function checkFileType(file: File, accepts: string[]) {
|
||||
// const reg = /\.(jpg|jpeg|png|gif|txt|doc|docx|xls|xlsx|xml)$/i;
|
||||
const reg = new RegExp('\\.(' + newTypes + ')$', 'i');
|
||||
|
||||
if (!reg.test(file.name)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return reg.test(file.name);
|
||||
}
|
||||
|
||||
export function checkImgType(file: File) {
|
||||
return /\.(jpg|jpeg|png|gif)$/i.test(file.name);
|
||||
return isImgTypeByName(file.name);
|
||||
}
|
||||
|
||||
export function isImgTypeByName(name: string) {
|
||||
return /\.(jpg|jpeg|png|gif)$/i.test(name);
|
||||
}
|
||||
|
||||
export function getBase64WithFile(file: File) {
|
||||
return new Promise<{
|
||||
result: string;
|
||||
|
@ -6,6 +6,11 @@
|
||||
// &.ant-btn-primary:not(.ant-btn-link) {
|
||||
// box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.12), 0 2px 4px 0 rgba(0, 0, 0, 0.08) !important;
|
||||
// }
|
||||
// &-group {
|
||||
// .ant-btn:not(:first-child) {
|
||||
// bottom: 1px;
|
||||
// }
|
||||
// }
|
||||
|
||||
&-primary {
|
||||
color: @white;
|
||||
|
@ -16,4 +16,4 @@
|
||||
@page-loading-z-index: 10000;
|
||||
|
||||
// left-menu
|
||||
@app-menu-item-height: 46px;
|
||||
@app-menu-item-height: 44px;
|
||||
|
@ -4,6 +4,9 @@ const menu: MenuModule = {
|
||||
menu: {
|
||||
name: '组件',
|
||||
path: '/comp',
|
||||
tag: {
|
||||
dot: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'basic',
|
||||
@ -38,10 +41,13 @@ const menu: MenuModule = {
|
||||
path: 'strength-meter',
|
||||
name: '密码强度组件',
|
||||
},
|
||||
// {
|
||||
// path: 'upload',
|
||||
// name: '上传组件',
|
||||
// },
|
||||
{
|
||||
path: 'upload',
|
||||
name: '上传组件',
|
||||
tag: {
|
||||
content: 'new',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'scroll',
|
||||
name: '滚动组件',
|
||||
|
8
src/types/global.d.ts
vendored
8
src/types/global.d.ts
vendored
@ -1,5 +1,9 @@
|
||||
declare interface Fn<T = any> {
|
||||
(...arg: T[]): T;
|
||||
declare interface Fn<T = any, R = T> {
|
||||
(...arg: T[]): R;
|
||||
}
|
||||
|
||||
declare interface PromiseFn<T = any, R = T> {
|
||||
(...arg: T[]): Promise<R>;
|
||||
}
|
||||
|
||||
// 任意对象
|
||||
|
@ -1,5 +1,25 @@
|
||||
import { dataURLtoBlob } from './stream';
|
||||
import { dataURLtoBlob, urlToBase64 } from './stream';
|
||||
|
||||
/**
|
||||
* Download online pictures
|
||||
* @param url
|
||||
* @param filename
|
||||
* @param mime
|
||||
* @param bom
|
||||
*/
|
||||
export function downloadByOnlineUrl(url: string, filename: string, mime?: string, bom?: BlobPart) {
|
||||
urlToBase64(url).then((base64) => {
|
||||
downloadByBase64(base64, filename, mime, bom);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Download pictures based on base64
|
||||
* @param buf
|
||||
* @param filename
|
||||
* @param mime
|
||||
* @param bom
|
||||
*/
|
||||
export function downloadByBase64(buf: string, filename: string, mime?: string, bom?: BlobPart) {
|
||||
const base64Buf = dataURLtoBlob(buf);
|
||||
downloadByData(base64Buf, filename, mime, bom);
|
@ -13,3 +13,29 @@ export function dataURLtoBlob(base64Buf: string): Blob {
|
||||
}
|
||||
return new Blob([u8arr], { type: mime });
|
||||
}
|
||||
|
||||
/**
|
||||
* img url to base64
|
||||
* @param url
|
||||
*/
|
||||
export function urlToBase64(url: string, mineType?: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let canvas = document.createElement('CANVAS') as Nullable<HTMLCanvasElement>;
|
||||
const ctx = canvas!.getContext('2d');
|
||||
|
||||
const img = new Image();
|
||||
img.crossOrigin = '';
|
||||
img.onload = function () {
|
||||
if (!canvas || !ctx) {
|
||||
return reject();
|
||||
}
|
||||
canvas.height = img.height;
|
||||
canvas.width = img.width;
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const dataURL = canvas.toDataURL(mineType || 'image/png');
|
||||
canvas = null;
|
||||
resolve(dataURL);
|
||||
};
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
|
@ -118,11 +118,8 @@ export class VAxios {
|
||||
Object.keys(params.data).forEach((key) => {
|
||||
if (!params.data) return;
|
||||
const value = params.data[key];
|
||||
// support key-value array data
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item) => {
|
||||
// { list: [ 11, 22 ] }
|
||||
// formData.append('list[]', 11);
|
||||
formData.append(`${key}[]`, item);
|
||||
});
|
||||
return;
|
||||
|
@ -160,20 +160,18 @@ const transform: AxiosTransform = {
|
||||
try {
|
||||
if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
|
||||
createMessage.error('接口请求超时,请刷新页面重试!');
|
||||
return;
|
||||
}
|
||||
if (err && err.includes('Network Error')) {
|
||||
createErrorModal({
|
||||
title: '网络异常',
|
||||
content: '请检查您的网络连接是否正常!',
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
checkStatus(error.response && error.response.status, msg);
|
||||
return error;
|
||||
return Promise.reject(error);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -38,4 +38,5 @@ export interface UploadFileParams {
|
||||
file: File | Blob;
|
||||
// 文件名
|
||||
filename?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
@ -1,17 +1,60 @@
|
||||
<template>
|
||||
<div class="p-4">
|
||||
<UploadContainer :maxSize="5" />
|
||||
<a-alert message="基础示例" class="my-5"></a-alert>
|
||||
<BasicUpload :maxSize="20" :maxNumber="10" @change="handleChange" :api="uploadApi" />
|
||||
|
||||
<a-alert message="嵌入表单,加入表单校验" class="my-5"></a-alert>
|
||||
|
||||
<BasicForm @register="register" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { UploadContainer } from '/@/components/Upload/index';
|
||||
import { defineComponent, h } from 'vue';
|
||||
import { BasicUpload } from '/@/components/Upload';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
|
||||
|
||||
import { uploadApi } from '/@/api/sys/upload';
|
||||
// import { Alert } from 'ant-design-vue';
|
||||
|
||||
const schemas: FormSchema[] = [
|
||||
{
|
||||
field: 'field1',
|
||||
component: 'Input',
|
||||
label: '字段1',
|
||||
colProps: {
|
||||
span: 8,
|
||||
},
|
||||
rules: [{ required: true, type: 'array', message: '请选择上传文件' }],
|
||||
render: ({ model, field }) => {
|
||||
return h(BasicUpload, {
|
||||
value: model[field],
|
||||
api: uploadApi,
|
||||
onChange: (val: string[]) => {
|
||||
model[field] = val;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
export default defineComponent({
|
||||
components: { UploadContainer },
|
||||
components: { BasicUpload, BasicForm },
|
||||
setup() {
|
||||
return {};
|
||||
const { createMessage } = useMessage();
|
||||
const [register] = useForm({
|
||||
labelWidth: 120,
|
||||
schemas,
|
||||
actionColOptions: {
|
||||
span: 16,
|
||||
},
|
||||
});
|
||||
return {
|
||||
handleChange: (list: string[]) => {
|
||||
createMessage.info(`已上传文件${JSON.stringify(list)}`);
|
||||
},
|
||||
uploadApi,
|
||||
register,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -8,11 +8,21 @@
|
||||
|
||||
<a-alert message="base64流下载" />
|
||||
<a-button type="primary" class="my-4" @click="handleDownloadByBase64"> base64流下载 </a-button>
|
||||
|
||||
<a-alert message="图片Url下载,如果有跨域问题,需要处理图片跨域" />
|
||||
<a-button type="primary" class="my-4" @click="handleDownloadByOnlineUrl">
|
||||
图片Url下载
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { downloadByUrl, downloadByData, downloadByBase64 } from '/@/utils/file/FileDownload';
|
||||
import {
|
||||
downloadByUrl,
|
||||
downloadByData,
|
||||
downloadByBase64,
|
||||
downloadByOnlineUrl,
|
||||
} from '/@/utils/file/download';
|
||||
import imgBase64 from './imgBase64';
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
@ -24,15 +34,28 @@
|
||||
url: 'https://codeload.github.com/anncwb/vue-vben-admin-doc/zip/master',
|
||||
target: '_self',
|
||||
});
|
||||
|
||||
downloadByUrl({
|
||||
url: 'https://vebn.oss-cn-beijing.aliyuncs.com/vben/logo.png',
|
||||
target: '_self',
|
||||
});
|
||||
}
|
||||
|
||||
function handleDownloadByBase64() {
|
||||
downloadByBase64(imgBase64, 'logo.png');
|
||||
}
|
||||
|
||||
function handleDownloadByOnlineUrl() {
|
||||
downloadByOnlineUrl(
|
||||
'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5944817f47b8408e9f1442ece49d68ca~tplv-k3u1fbpfcp-watermark.image',
|
||||
'logo.png'
|
||||
);
|
||||
}
|
||||
return {
|
||||
handleDownloadByUrl,
|
||||
handleDownByData,
|
||||
handleDownloadByBase64,
|
||||
handleDownloadByOnlineUrl,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
58
yarn.lock
58
yarn.lock
@ -1050,10 +1050,10 @@
|
||||
resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.2.tgz#c4a95ddc06ca9b9496df03604e66fdefb39f4c4b"
|
||||
integrity sha512-BybEHU5/I9EQ0CcwKAqmreZ2bMnAXrqLCTptAc6vPetHMbrXdZfejP5mt57e/8PNSt/qE7BHniU5PCYA+PGIHw==
|
||||
|
||||
"@iconify/json@^1.1.254":
|
||||
version "1.1.256"
|
||||
resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.256.tgz#0f138d421ab12faca2fdd49aaf4fbc0122db08e3"
|
||||
integrity sha512-CeLKbKL3lvq8afhR3LEyaBqXZDC52fgU0Ij3LbTRCwPUsumLNzhXA7MzN/f0JDYfXm9LShkfpgMcm00wQaANgg==
|
||||
"@iconify/json@^1.1.258":
|
||||
version "1.1.258"
|
||||
resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.258.tgz#392064ae8fd4c6d542c21bb4d0d57d5860f38abb"
|
||||
integrity sha512-x5DKhRrg8v1NWmClWa8zA80gWQ9xevivsUAF4s8CyAl/ZplBsEE1funKuuVcIKjexyE1UXb7uFWrUKt1fB5n1A==
|
||||
|
||||
"@koa/cors@^3.1.0":
|
||||
version "3.1.0"
|
||||
@ -1316,10 +1316,10 @@
|
||||
"@types/qs" "*"
|
||||
"@types/serve-static" "*"
|
||||
|
||||
"@types/fs-extra@^9.0.2":
|
||||
version "9.0.3"
|
||||
resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.3.tgz#9996e5cce993508c32325380b429f04a1327523e"
|
||||
integrity sha512-NKdGoXLTFTRED3ENcfCsH8+ekV4gbsysanx2OPbstXVV6fZMgUCqTxubs6I9r7pbOJbFgVq1rpFtLURjKCZWUw==
|
||||
"@types/fs-extra@^9.0.4":
|
||||
version "9.0.4"
|
||||
resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.4.tgz#12553138cf0438db9a31cdc8b0a3aa9332eb67aa"
|
||||
integrity sha512-50GO5ez44lxK5MDH90DYHFFfqxH7+fTqEEnvguQRzJ/tY9qFrMSHLiYHite+F3SNmf7+LHC1eMXojuD+E3Qcyg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
@ -1725,18 +1725,18 @@
|
||||
vscode-languageserver-textdocument "^1.0.1"
|
||||
vscode-uri "^2.1.2"
|
||||
|
||||
"@vueuse/core@^4.0.0-beta.40":
|
||||
version "4.0.0-beta.40"
|
||||
resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-beta.40.tgz#7efdc15c1b994647dff7ae65c0ca573d96ce9b28"
|
||||
integrity sha512-FOTOUrXAAp0NOmy8hMlP1HpUhnB8LeRJZDOEUl/A9gKMDwWvPTEvxKsDAIzSa4s7I0MapVzfeP3soNCNfl9+vQ==
|
||||
"@vueuse/core@^4.0.0-beta.41":
|
||||
version "4.0.0-beta.41"
|
||||
resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-beta.41.tgz#0058aed5ade75ae2866283498009ad5172cbae84"
|
||||
integrity sha512-CgUih65PzYScorm1S4F93e6XXm+qxA8GrRLOSB1kXaqtP6vXedwkBxKkNEYNACx4reL4VEHqM/BrM6FajXkQUg==
|
||||
dependencies:
|
||||
"@vueuse/shared" "4.0.0-beta.40"
|
||||
"@vueuse/shared" "4.0.0-beta.41"
|
||||
vue-demi latest
|
||||
|
||||
"@vueuse/shared@4.0.0-beta.40":
|
||||
version "4.0.0-beta.40"
|
||||
resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-beta.40.tgz#76e9b52228159e7ec88df2c8f4eea8fce1a42ec3"
|
||||
integrity sha512-Ay71viUTXs0XX2hQ04kEExhpsCrw3KankBMP7euorsPjuQmIZjUA4NNOb45UAudg+uF5HXLpgWLvwb4cMOLHnQ==
|
||||
"@vueuse/shared@4.0.0-beta.41":
|
||||
version "4.0.0-beta.41"
|
||||
resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-beta.41.tgz#395782ea2e580f1fc9488d25c89bd09f70170b25"
|
||||
integrity sha512-dqnuEPPC3OUJ6L6rhMiOCuPWIR698DtdwOydwCZBISsG2V6gZ2QFND6xtRwLib6/lhUMYVYPwIz3hPjlx7BIzw==
|
||||
dependencies:
|
||||
vue-demi latest
|
||||
|
||||
@ -1850,10 +1850,10 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
|
||||
ant-design-vue@^2.0.0-beta.15:
|
||||
version "2.0.0-beta.15"
|
||||
resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.0-beta.15.tgz#3c787dabb70a33885d0e751e58f9a5610ed06134"
|
||||
integrity sha512-OxZy+ZYU3LauIL4Rhqwy441K/iD++Cit6upnQy5+LVUrX0PSObPqPqMWVpncbAmJJYTEz88gkvgGeYqBdzouWA==
|
||||
ant-design-vue@^2.0.0-rc.1:
|
||||
version "2.0.0-rc.1"
|
||||
resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.0-rc.1.tgz#2ef02475f3aa4c1474f2fe3cf44a52c34787be02"
|
||||
integrity sha512-iKXkFtTHarvLHV7LWmYh6g/Cmkv+xK+vS621A1Qvg37Z6lCGg3K9BGAizmklAYzOTiPz0Ltt63eSiNqYMGh52g==
|
||||
dependencies:
|
||||
"@ant-design-vue/use" "^0.0.1-0"
|
||||
"@ant-design/icons-vue" "^5.1.5"
|
||||
@ -8109,10 +8109,10 @@ vary@^1.1.2:
|
||||
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
|
||||
|
||||
vditor@^3.6.0:
|
||||
version "3.6.1"
|
||||
resolved "https://registry.npmjs.org/vditor/-/vditor-3.6.1.tgz#b0b510f23d0cf0e5d8b3d36924e40400de96f692"
|
||||
integrity sha512-83GdGLIWrV1x04aK8DO9aZidqQfmuGXXUbxSCuQxRla+T9KfnFRmJwfqIxXQm8h+4jUBCFL38e8PqLa3fBOf9w==
|
||||
vditor@^3.6.2:
|
||||
version "3.6.2"
|
||||
resolved "https://registry.npmjs.org/vditor/-/vditor-3.6.2.tgz#ee6011efa3ec563c6356ed82efbf2e00ba2e35c6"
|
||||
integrity sha512-HPHHun5+IXmYGMKDWcUD83VfP1Qfncz7DmaIKoWpluJgE8ve7s+4RbFBcaEpYPXYzIuL2UTHoMnIjmTPbenOCA==
|
||||
dependencies:
|
||||
diff-match-patch "^1.0.5"
|
||||
|
||||
@ -8272,10 +8272,10 @@ vue-i18n@^9.0.0-beta.6:
|
||||
dependencies:
|
||||
source-map "^0.6.1"
|
||||
|
||||
vue-router@^4.0.0-rc.2:
|
||||
version "4.0.0-rc.2"
|
||||
resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-rc.2.tgz#8545cab76a05ca4f6dffbe6c6a671a4dbf585ab2"
|
||||
integrity sha512-51mBp39rzBFpk1nyU9SkhPcwR67gBzWIH8p3pyeDmtNYgWzGF3q8MneD/xbMwsfTQkw2H1qBk6uwRaVy3M8Nxw==
|
||||
vue-router@^4.0.0-rc.3:
|
||||
version "4.0.0-rc.3"
|
||||
resolved "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-rc.3.tgz#70d18e90030bc6a25e81a30401d673223998ec6b"
|
||||
integrity sha512-NnPqWIfanEhJC4wu8BEFBmnEDIrx9ST0/HtmBiE+oV2MQlhyRk1TmdttWwVqx6Sh7kONsrI10GQV9l3YEkcWXg==
|
||||
|
||||
vue-types@^3.0.0:
|
||||
version "3.0.1"
|
||||
|
Loading…
Reference in New Issue
Block a user