CommonFileList.vue 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. <template>
  2. <view class="ui-file-list" v-if="filteredFiles && filteredFiles.length">
  3. <view
  4. class="file-item"
  5. v-for="(file, index) in filteredFiles"
  6. :key="file.id || index"
  7. @click="handlePreview(file)"
  8. >
  9. <view class="file-icon">
  10. <uv-icon name="file-text" color="#1c9bfd" size="64rpx"></uv-icon>
  11. </view>
  12. <view class="file-info">
  13. <text class="f-name">{{ file.fileName || file.name }}</text>
  14. <view class="f-meta">
  15. <text class="f-type" v-if="file.fileType">{{ file.fileType }}</text>
  16. <text class="f-size" v-if="file.fileSize">{{ formatSize(file.fileSize) }}</text>
  17. <text class="f-date" v-if="file.createdTime || file.createTime">
  18. {{ (file.createdTime || file.createTime).slice(0, 10) }}
  19. </text>
  20. </view>
  21. </view>
  22. <view class="file-action">
  23. <uv-icon name="arrow-right" color="#c0c4cc" size="32rpx"></uv-icon>
  24. </view>
  25. </view>
  26. </view>
  27. <view v-else class="empty-wrap">
  28. <uv-empty mode="data" text="暂无附件信息"></uv-empty>
  29. </view>
  30. </template>
  31. <script setup lang="ts">
  32. import { computed } from 'vue';
  33. import { previewFile } from '@/utils/file';
  34. /**
  35. * UI 通用组件 - 附件列表呈现
  36. * 封装统一样式的附件项及其预览联通逻辑。
  37. */
  38. const props = defineProps<{
  39. /** 附件数组(支持 ProjectFile 类型及类似结构) */
  40. files: any[];
  41. }>();
  42. const filteredFiles = computed(() => {
  43. if (!props.files) return [];
  44. return props.files.filter((file) => file.fileName || file.name);
  45. });
  46. /**
  47. * 实现点击预览逻辑
  48. */
  49. const handlePreview = (file: any) => {
  50. const url = file.fileUrl || file.url;
  51. const name = file.fileName || file.name;
  52. if (!url) return uni.showToast({ title: '文件地址不存在', icon: 'none' });
  53. previewFile(url, name);
  54. };
  55. /**
  56. * 文件大小美化展示
  57. */
  58. const formatSize = (size: number) => {
  59. if (!size) return '';
  60. if (size < 1024) return size + 'B';
  61. if (size < 1024 * 1024) return (size / 1024).toFixed(2) + 'KB';
  62. return (size / (1024 * 1024)).toFixed(2) + 'MB';
  63. };
  64. </script>
  65. <style lang="scss" scoped>
  66. .ui-file-list {
  67. display: flex;
  68. flex-direction: column;
  69. .file-item {
  70. display: flex;
  71. align-items: center;
  72. padding: 30rpx 24rpx;
  73. background-color: #f8fafc;
  74. border-radius: 16rpx;
  75. margin-bottom: 20rpx;
  76. border: 2rpx solid transparent;
  77. transition: all 0.2s;
  78. &:last-child {
  79. margin-bottom: 0;
  80. }
  81. &:active {
  82. background-color: #f1f5f9;
  83. transform: scale(0.98);
  84. border-color: #e2e8f0;
  85. }
  86. .file-icon {
  87. width: 80rpx;
  88. height: 80rpx;
  89. display: flex;
  90. justify-content: center;
  91. align-items: center;
  92. margin-right: 24rpx;
  93. background-color: #fff;
  94. border-radius: 12rpx;
  95. }
  96. .file-info {
  97. flex: 1;
  98. display: flex;
  99. flex-direction: column;
  100. overflow: hidden;
  101. .f-name {
  102. font-size: 28rpx;
  103. color: #343a3f;
  104. font-weight: 500;
  105. margin-bottom: 12rpx;
  106. word-break: break-all;
  107. text-overflow: ellipsis;
  108. display: -webkit-box;
  109. -webkit-line-clamp: 1;
  110. -webkit-box-orient: vertical;
  111. line-clamp: 1;
  112. overflow: hidden;
  113. }
  114. .f-meta {
  115. display: flex;
  116. font-size: 24rpx;
  117. color: #94a3b8;
  118. gap: 16rpx;
  119. .f-type {
  120. color: #1c9bfd;
  121. background-color: rgba(28, 155, 253, 0.08);
  122. padding: 2rpx 10rpx;
  123. border-radius: 6rpx;
  124. }
  125. }
  126. }
  127. .file-action {
  128. margin-left: 16rpx;
  129. }
  130. }
  131. }
  132. .empty-wrap {
  133. padding: 80rpx 0;
  134. display: flex;
  135. justify-content: center;
  136. align-items: center;
  137. }
  138. </style>