diff --git a/CHANGELOG.md b/CHANGELOG.md index 61b8eec6d429f385b3d6b587e4d3cc2b0989786f..2bfd587f645a5f00e2ce444643edd77b5146f979 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ ## [Unreleased] +### Added + +- 新增文件上传是否显示加载动画编辑器参数showloading,优化上传大文件用户体验 + +### Fixed + +- 修复视图loading指令弹框显示时长异常,调整默认显示2s为不关闭,而是由控制器关闭 +- 修复级联下拉回显异常,当代码表数据过大时computed响应丢失 + ## [0.7.41-alpha.11] - 2025-08-29 ### Added diff --git a/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx b/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx index 2b00ff32f75a29f9f628b1840fe4e7bd0c8161ea..1dc4e891367d4d24e8508ef1359957c9cb5f57c5 100644 --- a/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx +++ b/src/editor/dropdown-list/ibiz-cascader-dropdown/ibiz-cascader-dropdown.tsx @@ -1,4 +1,4 @@ -import { ref, Ref, defineComponent, computed } from 'vue'; +import { ref, Ref, defineComponent, watch } from 'vue'; import { getDropdownProps, getEditorEmits, @@ -29,6 +29,8 @@ export const IBizCascaderDropdown = defineComponent({ const treeData: Ref = ref([]); // 代码表map数据 const codeListMap = new Map(); + // 当前值 + const curValue = ref(''); // 选中值 const selectValue: Ref = ref(null); const show = ref(false); @@ -79,6 +81,10 @@ export const IBizCascaderDropdown = defineComponent({ // 存在父值属性时转换为树型数据,否则平铺 const { pvaluefield } = c.editorParams; treeData.value = transformTreeData(codeList, pvaluefield); + if (props.value) { + const item = codeListMap.get(props.value); + curValue.value = item?.text || ''; + } }; loadCodeList(); @@ -96,11 +102,14 @@ export const IBizCascaderDropdown = defineComponent({ emit('change', value); }; - // 当前值 - const curValue = computed(() => { - const item = codeListMap.get(props.value || ''); - return item?.text || ''; - }); + watch( + () => props.value, + newVal => { + const item = codeListMap.get(newVal || ''); + curValue.value = item?.text || ''; + }, + { immediate: true }, + ); const onBlur = () => { emit('blur'); diff --git a/src/editor/upload/ibiz-file-upload/ibiz-file-upload.tsx b/src/editor/upload/ibiz-file-upload/ibiz-file-upload.tsx index c8d2e0ccd65841437638d6f708e387cbee1d3438..e1548f0c50a58b5fba383c857c7966a21afa7c5f 100644 --- a/src/editor/upload/ibiz-file-upload/ibiz-file-upload.tsx +++ b/src/editor/upload/ibiz-file-upload/ibiz-file-upload.tsx @@ -16,6 +16,7 @@ import { UploadEditorController } from '../upload-editor.controller'; * @editorparams {name:accept,parameterType:string,description:允许上传的文件类型,van-uploader组件的accept属性} * @editorparams {name:uploadParams,parameterType:IData,description:上传参数} * @editorparams {name:exportParams,parameterType:IData,description:下载参数} + * @editorparams {name:showloading,parameterType:boolean,description:是否显示加载动画} * @ignoreprops autoFocus | overflowMode * @ignoreemits blur | focus | enter | infoTextChange * @@ -32,6 +33,7 @@ export const IBizFileUpload = defineComponent({ uploadUrl, headers, files, + loading, onRemove, beforeUpload, onDownload, @@ -84,6 +86,7 @@ export const IBizFileUpload = defineComponent({ uploadUrl, headers, files, + loading, onRemove, beforeUpload, onDownload, @@ -120,6 +123,7 @@ export const IBizFileUpload = defineComponent({ !this.readonly && !this.disabled && ( { */ public exportParams?: IParams; + /** + * 显示加载动画 + */ + public showLoading: boolean = false; + /** * 文件类型 * @@ -106,8 +111,14 @@ export class UploadEditorController extends EditorController { this.multiple = false; } if (this.editorParams) { - const { isDrag, multiple, accept, uploadParams, exportParams } = - this.editorParams; + const { + isDrag, + multiple, + accept, + uploadParams, + exportParams, + showloading, + } = this.editorParams; if (isDrag) { this.isDrag = Boolean(isDrag); } @@ -117,6 +128,9 @@ export class UploadEditorController extends EditorController { if (accept) { this.accept = accept; } + if (showloading) { + this.showLoading = Boolean(showloading); + } if (uploadParams) { try { this.uploadParams = JSON.parse(uploadParams); diff --git a/src/editor/upload/use/use-ibiz-upload.ts b/src/editor/upload/use/use-ibiz-upload.ts deleted file mode 100644 index 66abbfdff4cd94b6af0542becc28a76fa1f6b0ee..0000000000000000000000000000000000000000 --- a/src/editor/upload/use/use-ibiz-upload.ts +++ /dev/null @@ -1,307 +0,0 @@ -/* eslint-disable no-param-reassign */ -import { - HttpResponse, - isImage, - IUploadFile, - uploadFile, -} from '@ibiz-template/core'; -import { Ref, ref, watch } from 'vue'; -import { UploadEditorController } from '../upload-editor.controller'; - -export type FileInfo = { - name: string; - id: string; - status?: 'uploading' | 'finished' | 'fail' | 'cancel'; - percentage?: number; - url?: string; - /** - * 文件名(不带后缀) - */ - fileName?: string; - /** - * 文件类型(拓展名) - */ - fileExt?: string; - /** - * 是否是图片 - */ - isImage?: boolean; -}; - -/** - * 格式化文件信息 - * - * @author lxm - * @date 2022-11-18 15:11:38 - * @param {FileInfo} file - */ -export function formatFileInfo(file: FileInfo, downloadUrl: string): FileInfo { - file.url = downloadUrl.replace('%fileId%', file.id!); - if (!file.status) { - // 不存在时为回填回来的数据默认给他finished - file.status = 'finished'; - } - if (!file.fileName) { - const index = file.name.lastIndexOf('.'); - file.fileName = file.name.substring(0, index); - file.fileExt = file.name.substring(index); - file.isImage = isImage(file.name); - } - return file as FileInfo; -} - -/** - * 文件上传组件初始化,解析props并得到downloadUrl、uploadUrl、fileList - * - * @author lxm - * @date 2022-11-21 10:11:01 - * @export - * @param {{ - * data: Ref; - * value: Ref; - * controller: Ref; - * }} props - * @returns {*} - */ -export function useIBizUploadInit(props: { - data: Ref; - value: Ref; - controller: Ref; -}): { - downloadUrl: Ref; - uploadUrl: Ref; - valueList: Ref; -} { - // 上传文件路径 - const uploadUrl: Ref = ref(''); - - // 下载文件路径 - const downloadUrl: Ref = ref(''); - - // 文件列表 - const valueList: Ref = ref([]); - - // data响应式变更基础路径 - watch( - props.data, - newVal => { - if (newVal) { - const urls = ibiz.util.file.calcFileUpDownUrl( - props.controller.value.context, - props.controller.value.params, - newVal, - props.controller.value.editorParams, - ); - uploadUrl.value = urls.uploadUrl; - downloadUrl.value = urls.downloadUrl; - } - }, - { immediate: true, deep: true }, - ); - - // 值响应式变更 - watch( - props.value, - newVal => { - valueList.value = !newVal ? [] : JSON.parse(newVal); - if (valueList.value.length && downloadUrl.value) { - valueList.value.forEach((file: FileInfo) => { - formatFileInfo(file, downloadUrl.value); - }); - } - }, - { immediate: true }, - ); - - watch( - downloadUrl, - newVal => { - // 下载基础路径变更时全部url重算 - if (newVal && valueList.value.length) { - valueList.value.forEach((file: FileInfo) => { - formatFileInfo(file, newVal); - }); - } - }, - { immediate: true }, - ); - - return { - downloadUrl, - uploadUrl, - valueList, - }; -} - -/** - * 使用文件上传功能,传递外部已存在的文件集合,上传下载基础路径 - * - * @author lxm - * @date 2022-11-21 10:11:01 - * @export - * @param {{ - * downloadUrl: Ref; - * uploadUrl: Ref; - * value: Ref< - * { - * name: string; - * id: string; - * url?: string; - * }[] - * >; - * }} opts - * @returns {*} - */ -export function useIBizUpload(opts: { - downloadUrl: Ref; - uploadUrl: Ref; - value: Ref< - { - name: string; - id: string; - url?: string; - }[] - >; - multiple?: boolean; - accept?: string; -}): { - selectFile: () => void; - fileList: Ref< - { - name: string; - id: string; - status?: 'uploading' | 'finished' | 'fail' | 'cancel' | undefined; - percentage?: number | undefined; - url?: string | undefined; - fileName?: string | undefined; - fileExt?: string | undefined; - isImage?: boolean | undefined; - }[] - >; - uploadState: Ref<'loading' | 'undo' | 'done'>; -} { - const uploadState = ref<'undo' | 'loading' | 'done'>('undo'); - const fileList = ref([]); - const { downloadUrl, value, uploadUrl } = opts; - - // 初始化fileList - watch( - value, - newVal => { - if (newVal.length > 0) { - fileList.value = []; - newVal.forEach(item => { - fileList.value.push(formatFileInfo(item, downloadUrl.value)); - }); - } - }, - { - immediate: true, - deep: true, - }, - ); - - // 开始上传后记录文件 - const beforeUpload = (fileData: File[], files: IUploadFile[]) => { - files.forEach(file => { - fileList.value.push({ - name: file.name, - status: file.status, - percentage: file.percentage, - id: file.uid, - url: '', - }); - }); - return true; - }; - - /** - * 更新文件里的上传进度 - * - * @author lxm - * @date 2022-11-18 15:11:09 - * @param {IUploadFile[]} files - */ - const onProgress = (files: IUploadFile[]) => { - files.forEach(file => { - fileList.value.find(item => { - if (item.id === file.uid) { - item.percentage = file.percentage; - return true; - } - return false; - }); - }); - }; - - const onSuccess = (resultFiles: IUploadFile[], res: HttpResponse) => { - // 一次上传成功这一批的文件都成功 - resultFiles.forEach(file => { - fileList.value.find(item => { - if (item.id === file.uid) { - // 把用后台数据替换当前信息,并格式化信息 - item.status = file.status; - item.id = res.data.fileid; - item.name = res.data.filename; - formatFileInfo(item, downloadUrl.value); - return true; - } - return false; - }); - }); - }; - - const onError = (resultFiles: IUploadFile[], error: unknown) => { - // 一次报错这一批的文件都上传失败 - resultFiles.forEach(file => { - fileList.value.find(item => { - if (item.id === file.uid) { - // 更新错误状态 - item.status = file.status; - return true; - } - return false; - }); - }); - // 处理报错信息 - console.error(error); - }; - - const onFinish = (_resultFiles: IUploadFile[]) => { - fileList.value = fileList.value.filter(file => file.status === 'finished'); - uploadState.value = 'done'; - }; - - // 手动控制文件上传,绑定组件的upload - const selectFile = () => { - uploadFile({ - multiple: opts.multiple, - accept: opts.accept, - uploadUrl: uploadUrl.value, - beforeUpload, - progress: onProgress, - success: onSuccess, - error: onError, - finish: onFinish, - }); - }; - - return { - selectFile, - fileList, - uploadState, - }; -} - -export function openImagePreview(file: FileInfo): Promise { - return ibiz.overlay.modal( - 'ImagePreview', - { file }, - { - width: 'auto', - height: 'auto', - placement: 'center', - modalClass: 'ibiz-image-preview-modal', - }, - ); -} diff --git a/src/editor/upload/use/use-van-upload.ts b/src/editor/upload/use/use-van-upload.ts index 3fa1faf55adead7f42830d505fe1a09563c6afb5..88320b738ef1d7242e638664ef5f19caa2706e4c 100644 --- a/src/editor/upload/use/use-van-upload.ts +++ b/src/editor/upload/use/use-van-upload.ts @@ -31,6 +31,7 @@ export function useVanUpload( }[] >; limit: ComputedRef<1 | 9999>; + loading: Ref; onDownload: (file: IData) => void; onError: (...args: IData[]) => never; onRemove: (file: IData) => void; @@ -60,6 +61,9 @@ export function useVanUpload( // 下载文件路径 const downloadUrl: Ref = ref(''); + // 是否显示加载动画 + const loading = ref(false); + // 值响应式变更 watch( () => props.value, @@ -172,7 +176,6 @@ export function useVanUpload( }; const uploadFile = (file: IData) => { - console.log(file); // 创建一个空对象实例 const formData = new FormData(); // 调用append()方法添加数据 @@ -187,7 +190,6 @@ export function useVanUpload( }) .then(res => { if (res.status === 200) { - console.log(88, res); onSuccess(res.data); resolve(true); } else { @@ -203,6 +205,9 @@ export function useVanUpload( // 读取成功回调 const afterRead = async (file: IData | IData[]) => { + if (c.showLoading) { + loading.value = true; + } if (file.length && file.length > 0) { for (let i = 0; i < file.length; i++) { const fi = (file as IData[])[i]; @@ -212,6 +217,9 @@ export function useVanUpload( } else { await uploadFile(file); } + if (c.showLoading) { + loading.value = false; + } }; // 下载文件 @@ -231,6 +239,7 @@ export function useVanUpload( headers, files, limit, + loading, onDownload, onError, onRemove, diff --git a/src/util/directive/loading.ts b/src/util/directive/loading.ts index d450c9c3d794aab08b877f0ee16e01dc7be5a278..167fc6b103108794b02edc51d66878d5f6c091c0 100644 --- a/src/util/directive/loading.ts +++ b/src/util/directive/loading.ts @@ -32,6 +32,7 @@ const createInstance = (el: ElementLoading) => { message: `${ibiz.i18n.t('util.loading')}...`, forbidClick: true, teleport: el, + duration: 0, className: ns.b(), loadingType: 'spinner', overlayClass: ns.e('overlay'),