import type { UploadChangeParam, UploadFile, UploadProps, } from 'ant-design-vue'; import { defineComponent, h, ref, watch } from 'vue'; import { IconifyIcon } from '@vben/icons'; import { $t } from '@vben/locales'; import { isEmpty } from '@vben/utils'; import { Button, Upload } from '../antd'; import { previewImage } from './upload-preview'; /** * Upload 未提供自定义插槽时,补齐项目统一的默认上传按钮。 */ function createDefaultSlotsWithUpload(listType: string, placeholder: string) { switch (listType) { case 'picture-card': { return { default: () => placeholder, }; } default: { return { default: () => h( Button, { icon: h(IconifyIcon, { icon: 'ant-design:upload-outlined', class: 'mb-1 size-4', }), }, () => placeholder, ), }; } } } /** * 适配 Ant Design Vue Upload 到 Vben Form: * - 使用 modelValue 与表单值同步 * - 上传列表为空时回写 undefined * - 内置图片预览,非图片文件直接打开地址 */ export const withPreviewUpload = () => { return defineComponent({ name: Upload.name, emits: ['change', 'update:modelValue'], setup: ( props: any, { attrs, slots, emit }: { attrs: any; emit: any; slots: any }, ) => { const previewVisible = ref(false); const placeholder = attrs?.placeholder || $t(`ui.placeholder.upload`); const listType = attrs?.listType || attrs?.['list-type'] || 'text'; const fileList = ref( attrs?.fileList || attrs?.['file-list'] || [], ); const handleChange = async (event: UploadChangeParam) => { fileList.value = event.fileList; emit('change', event); emit( 'update:modelValue', event.fileList?.length ? fileList.value : undefined, ); }; const handlePreview = async (file: UploadFile) => { previewVisible.value = true; await previewImage(file, previewVisible, fileList); }; const renderUploadButton = (): any => { const isDisabled = attrs.disabled; // 禁用状态下只展示文件列表,不再渲染上传入口 if (isDisabled) { return null; } return isEmpty(slots) ? createDefaultSlotsWithUpload(listType, placeholder) : slots; }; // 监听 formApi.setValues 等外部回填,保持 Upload 内部 fileList 同步 watch( () => attrs.modelValue, (res) => { fileList.value = res; }, ); return () => h( Upload, { ...props, ...attrs, fileList: fileList.value, onChange: handleChange, onPreview: handlePreview, }, renderUploadButton(), ); }, }); };