1.0.1 • Published 1 month ago

@tanzhenxing/zx-ai-attachments v1.0.1

Weekly downloads
-
License
ISC
Repository
-
Last release
1 month ago

ZxAiAttachments 附件组件

用于展示一组附件信息集合的 Vue 3 组件,基于 uni-app 框架开发,支持全平台使用。

特性

  • 🎯 多平台兼容:支持 H5、微信小程序、其他小程序平台、App
  • 🎨 丰富的展示样式:支持换行、横向滚动、纵向滚动等布局方式
  • 📱 响应式设计:自适应不同屏幕尺寸
  • 🎭 文件类型识别:自动识别并展示不同文件类型的图标
  • 🔧 高度可定制:支持自定义样式、占位内容等
  • 无障碍支持:支持禁用状态和键盘导航
  • 📦 轻量级:纯 JavaScript 实现,无外部依赖

安装使用

1. 组件引入

<template>
  <zx-ai-attachments
    :items="attachments"
    @remove="handleRemove"
    @preview="handlePreview"
  />
</template>

<script setup>
import ZxAiAttachments from '@/components/zx-ai-attachments/zx-ai-attachments.vue';

const attachments = ref([
  {
    uid: '1',
    name: '文档.pdf',
    size: 1024 * 1024 * 2,
    type: 'application/pdf',
    status: 'done'
  }
]);

const handleRemove = (item) => {
  console.log('移除文件:', item);
};

const handlePreview = (item) => {
  console.log('预览文件:', item);
};
</script>

2. 全局注册(可选)

// main.js
import ZxAiAttachments from '@/components/zx-ai-attachments/zx-ai-attachments.vue';

app.component('ZxAiAttachments', ZxAiAttachments);

API 文档

Props

属性说明类型默认值版本
items附件列表Attachment[][]-
disabled是否禁用booleanfalse-
overflow文件列表超出时样式'wrap' \| 'scrollX' \| 'scrollY''wrap'-
placeholder没有文件时的占位信息string \| PlaceholderConfignull-
imageProps图片属性配置object{}-
customClass自定义样式类名string''-
customStyle自定义样式对象string \| object''-
classNames语义化样式类名Record<string, string>{}-
styles语义化样式对象Record<string, object>{}-

Attachment 类型定义

interface Attachment {
  uid: string;                    // 唯一标识
  name: string;                   // 文件名
  size?: number;                  // 文件大小(字节)
  type?: string;                  // 文件类型
  status?: 'uploading' | 'done' | 'error'; // 上传状态
  percent?: number;               // 上传进度(0-100)
  url?: string;                   // 文件 URL
  thumbUrl?: string;              // 缩略图 URL
  preview?: string;               // 预览 URL
}

PlaceholderConfig 类型定义

interface PlaceholderConfig {
  icon?: string;                  // 图标内容
  title?: string;                 // 标题
  description?: string;           // 描述
}

ClassNames 语义化类名

属性说明
list文件列表容器类名
item文件项容器类名
placeholder占位内容容器类名

Styles 语义化样式

属性说明
list文件列表容器样式
item文件项容器样式
placeholder占位内容容器样式

Events

事件名说明回调参数
remove点击移除文件时触发(item: Attachment) => void
preview点击预览文件时触发(item: Attachment) => void
click点击组件时触发() => void

Slots

插槽名说明参数
placeholder自定义占位内容{ type: 'inline' }

使用示例

基础用法

<template>
  <zx-ai-attachments
    :items="attachments"
    @remove="handleRemove"
    @preview="handlePreview"
  />
</template>

<script setup>
const attachments = ref([
  {
    uid: '1',
    name: '产品设计图.png',
    size: 1024 * 1024 * 2.5,
    type: 'image/png',
    status: 'done',
    url: 'https://example.com/image.png'
  },
  {
    uid: '2',
    name: '项目文档.pdf',
    size: 1024 * 1024 * 5.2,
    type: 'application/pdf',
    status: 'done'
  }
]);
</script>

占位信息

<template>
  <!-- 字符串占位 -->
  <zx-ai-attachments
    :items="[]"
    placeholder="请选择要上传的文件"
  />
  
  <!-- 对象配置占位 -->
  <zx-ai-attachments
    :items="[]"
    :placeholder="{
      icon: '☁️',
      title: '拖拽文件到此处',
      description: '支持图片、文档、音频、视频等格式'
    }"
  />
  
  <!-- 插槽自定义占位 -->
  <zx-ai-attachments :items="[]">
    <template #placeholder>
      <view class="custom-placeholder">
        <text>自定义占位内容</text>
      </view>
    </template>
  </zx-ai-attachments>
</template>

超出样式

<template>
  <!-- 换行显示(默认) -->
  <zx-ai-attachments
    :items="manyFiles"
    overflow="wrap"
  />
  
  <!-- 横向滚动 -->
  <zx-ai-attachments
    :items="manyFiles"
    overflow="scrollX"
  />
  
  <!-- 纵向滚动 -->
  <zx-ai-attachments
    :items="manyFiles"
    overflow="scrollY"
  />
</template>

上传状态

<template>
  <zx-ai-attachments :items="uploadingFiles" />
</template>

<script setup>
const uploadingFiles = ref([
  {
    uid: '1',
    name: '上传中.jpg',
    size: 1024 * 1024 * 3,
    status: 'uploading',
    percent: 65
  },
  {
    uid: '2',
    name: '上传失败.pdf',
    size: 1024 * 1024 * 5,
    status: 'error'
  },
  {
    uid: '3',
    name: '上传完成.docx',
    size: 1024 * 1024 * 2,
    status: 'done'
  }
]);
</script>

自定义样式

<template>
  <zx-ai-attachments
    :items="attachments"
    custom-class="my-attachments"
    :custom-style="{
      border: '2px solid #1677ff',
      borderRadius: '12px',
      padding: '16px'
    }"
    :class-names="{
      list: 'my-list',
      item: 'my-item',
      placeholder: 'my-placeholder'
    }"
    :styles="{
      list: { gap: '16px' },
      item: { borderColor: '#1677ff' }
    }"
  />
</template>

<style>
.my-attachments {
  background-color: #f0f8ff;
}

.my-list {
  padding: 8px;
}

.my-item {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
</style>

禁用状态

<template>
  <zx-ai-attachments
    :items="attachments"
    :disabled="true"
  />
</template>

平台兼容性

平台支持情况说明
H5✅ 完全支持支持所有功能
微信小程序✅ 完全支持支持所有功能
支付宝小程序✅ 完全支持支持所有功能
百度小程序✅ 完全支持支持所有功能
字节小程序✅ 完全支持支持所有功能
QQ 小程序✅ 完全支持支持所有功能
快手小程序✅ 完全支持支持所有功能
京东小程序✅ 完全支持支持所有功能
App (Android)✅ 完全支持支持所有功能
App (iOS)✅ 完全支持支持所有功能

最佳实践

1. 文件大小格式化

组件会自动格式化文件大小显示,建议传入准确的字节数:

const file = {
  uid: '1',
  name: 'document.pdf',
  size: 1024 * 1024 * 2.5, // 2.5MB
  // ...
};

2. 文件类型识别

组件会根据文件扩展名和 MIME 类型自动识别文件类型并显示对应图标:

// 推荐同时提供 name 和 type
const file = {
  name: 'document.pdf',
  type: 'application/pdf',
  // ...
};

3. 图片预览

对于图片文件,建议提供 urlthumbUrl 用于预览:

const imageFile = {
  uid: '1',
  name: 'photo.jpg',
  type: 'image/jpeg',
  url: 'https://example.com/photo.jpg',
  thumbUrl: 'https://example.com/photo_thumb.jpg', // 缩略图
  // ...
};

4. 上传进度

上传过程中可以实时更新进度:

const uploadFile = (file) => {
  const fileItem = {
    uid: generateUID(),
    name: file.name,
    size: file.size,
    status: 'uploading',
    percent: 0
  };
  
  // 模拟上传进度
  const timer = setInterval(() => {
    fileItem.percent += 10;
    if (fileItem.percent >= 100) {
      fileItem.status = 'done';
      clearInterval(timer);
    }
  }, 200);
};

5. 错误处理

上传失败时设置错误状态:

const handleUploadError = (fileItem) => {
  fileItem.status = 'error';
  fileItem.percent = 0;
};

6. 性能优化

对于大量文件,建议使用虚拟滚动或分页加载:

// 分页加载示例
const pageSize = 20;
const currentPage = ref(1);

const visibleFiles = computed(() => {
  const start = (currentPage.value - 1) * pageSize;
  const end = start + pageSize;
  return allFiles.value.slice(start, end);
});

注意事项

1. 样式隔离

组件使用 scoped 样式,如需自定义样式,请使用 :deep() 或全局样式:

<style scoped>
:deep(.zx-ai-attachments__item) {
  border-color: #1677ff;
}
</style>

2. 事件处理

移除文件时,组件不会自动从列表中删除,需要在父组件中处理:

const handleRemove = (item) => {
  const index = attachments.value.findIndex(file => file.uid === item.uid);
  if (index > -1) {
    attachments.value.splice(index, 1);
  }
};

3. 文件预览

组件只负责展示,文件预览功能需要在父组件中实现:

const handlePreview = (item) => {
  if (item.type?.startsWith('image/')) {
    // 图片预览
    uni.previewImage({
      urls: [item.url]
    });
  } else {
    // 其他文件预览
    uni.downloadFile({
      url: item.url,
      success: (res) => {
        uni.openDocument({
          filePath: res.tempFilePath
        });
      }
    });
  }
};

4. 内存管理

对于大文件或大量文件,注意及时清理不需要的引用:

// 组件销毁时清理
onUnmounted(() => {
  attachments.value = [];
});

常见问题

Q: 如何实现文件上传功能?

A: 组件只负责展示附件,上传功能需要结合 uni-app 的上传 API 实现:

const uploadFile = () => {
  uni.chooseFile({
    count: 1,
    success: (res) => {
      const file = res.tempFiles[0];
      const fileItem = {
        uid: Date.now().toString(),
        name: file.name,
        size: file.size,
        status: 'uploading',
        percent: 0
      };
      
      attachments.value.push(fileItem);
      
      uni.uploadFile({
        url: 'https://api.example.com/upload',
        filePath: file.path,
        name: 'file',
        success: (uploadRes) => {
          fileItem.status = 'done';
          fileItem.url = JSON.parse(uploadRes.data).url;
        },
        fail: () => {
          fileItem.status = 'error';
        }
      });
    }
  });
};

Q: 如何自定义文件图标?

A: 可以通过 CSS 覆盖默认图标样式:

<style>
:deep(.zx-attachment-item__file-icon--pdf) {
  background-image: url('path/to/custom-pdf-icon.png');
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}
</style>

Q: 如何限制文件类型和大小?

A: 在添加文件时进行验证:

const validateFile = (file) => {
  // 限制文件类型
  const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
  if (!allowedTypes.includes(file.type)) {
    uni.showToast({
      title: '不支持的文件类型',
      icon: 'none'
    });
    return false;
  }
  
  // 限制文件大小(10MB)
  const maxSize = 10 * 1024 * 1024;
  if (file.size > maxSize) {
    uni.showToast({
      title: '文件大小不能超过 10MB',
      icon: 'none'
    });
    return false;
  }
  
  return true;
};

Q: 如何实现拖拽上传?

A: 在 H5 平台可以使用原生拖拽 API:

<template>
  <view 
    class="drop-area"
    @drop="handleDrop"
    @dragover="handleDragOver"
  >
    <zx-ai-attachments :items="attachments" />
  </view>
</template>

<script setup>
const handleDragOver = (e) => {
  e.preventDefault();
};

const handleDrop = (e) => {
  e.preventDefault();
  const files = Array.from(e.dataTransfer.files);
  files.forEach(file => {
    if (validateFile(file)) {
      addFileToList(file);
    }
  });
};
</script>

更新日志

v1.0.0

  • 🎉 初始版本发布
  • ✨ 支持基础附件展示功能
  • ✨ 支持多种布局方式
  • ✨ 支持自定义样式
  • ✨ 支持占位内容配置
  • ✨ 支持上传状态展示
  • ✨ 全平台兼容

许可证

MIT License

1.0.1

1 month ago