AttachmentList.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <template>
  2. <view class="attachment-section mt20" v-if="computedList.length">
  3. <view class="section-title">{{ title }}</view>
  4. <view class="attachment-container">
  5. <view
  6. class="file-item"
  7. v-for="(file, index) in computedList"
  8. :key="index"
  9. @click="handlePreview(file)"
  10. >
  11. <view class="file-inner">
  12. <view class="file-icon">
  13. <uv-icon name="file-text" size="26" color="#1c9bfd"></uv-icon>
  14. </view>
  15. <view class="file-content">
  16. <text class="f-name">{{ file.fileName || file.name || '-' }}</text>
  17. <view class="f-meta">
  18. <text class="f-tag">{{ file.fileType || file.type || '附件资料' }}</text>
  19. <text class="f-version" v-if="file.fileVersion">v{{ file.fileVersion }}</text>
  20. <text class="f-required" v-if="file.required">必填</text>
  21. <text class="f-time" v-if="file.createTime || file.fileVersionDate">
  22. {{ formatDate(file.createTime || file.fileVersionDate) }}
  23. </text>
  24. </view>
  25. </view>
  26. <view class="file-arrow">
  27. <uv-icon name="arrow-right" size="14" color="#cbd5e1"></uv-icon>
  28. </view>
  29. </view>
  30. </view>
  31. </view>
  32. </view>
  33. </template>
  34. <script setup lang="ts">
  35. import { computed } from 'vue';
  36. import { previewFile } from '@/utils/file';
  37. import { formatDate } from '@/utils/date';
  38. const props = defineProps({
  39. list: {
  40. type: Array as any,
  41. default: () => []
  42. },
  43. // 支持传入单个文件对象
  44. file: {
  45. type: Object as any,
  46. default: null
  47. },
  48. title: {
  49. type: String,
  50. default: '附件资料'
  51. }
  52. });
  53. const computedList = computed(() => {
  54. if (props.list && props.list.length) {
  55. return props.list;
  56. }
  57. if (props.file) {
  58. return [props.file];
  59. }
  60. return [];
  61. });
  62. const handlePreview = (file: any) => {
  63. const url = file.fileUrl || file.url;
  64. const name = file.fileName || file.name;
  65. if (url) {
  66. previewFile(url, name);
  67. }
  68. };
  69. </script>
  70. <style lang="scss" scoped>
  71. .attachment-section {
  72. background: #fff;
  73. padding: 30rpx 24rpx;
  74. border-radius: 16rpx;
  75. .section-title {
  76. font-size: 30rpx;
  77. font-weight: bold;
  78. color: #1a1a1a;
  79. margin-bottom: 24rpx;
  80. position: relative;
  81. padding-left: 20rpx;
  82. &::before {
  83. content: "";
  84. position: absolute;
  85. left: 0;
  86. top: 50%;
  87. transform: translateY(-50%);
  88. width: 6rpx;
  89. height: 28rpx;
  90. background: #1c9bfd;
  91. border-radius: 4rpx;
  92. }
  93. }
  94. }
  95. .attachment-container {
  96. .file-item {
  97. margin-bottom: 20rpx;
  98. &:last-child { margin-bottom: 0; }
  99. .file-inner {
  100. display: flex;
  101. align-items: center;
  102. padding: 24rpx;
  103. background: #f8fafc;
  104. border-radius: 12rpx;
  105. border: 1rpx solid #f1f5f9;
  106. transition: all 0.2s;
  107. &:active {
  108. background: #f0f9ff;
  109. border-color: #1c9bfd;
  110. opacity: 0.8;
  111. }
  112. .file-icon {
  113. width: 80rpx;
  114. height: 80rpx;
  115. background: #eff6ff;
  116. border-radius: 12rpx;
  117. display: flex;
  118. align-items: center;
  119. justify-content: center;
  120. margin-right: 20rpx;
  121. flex-shrink: 0;
  122. }
  123. .file-content {
  124. flex: 1;
  125. overflow: hidden;
  126. display: flex;
  127. flex-direction: column;
  128. .f-name {
  129. font-size: 28rpx;
  130. color: #333;
  131. font-weight: 500;
  132. margin-bottom: 8rpx;
  133. display: block;
  134. overflow: hidden;
  135. text-overflow: ellipsis;
  136. white-space: nowrap;
  137. }
  138. .f-meta {
  139. display: flex;
  140. align-items: center;
  141. .f-tag {
  142. font-size: 20rpx;
  143. color: #1c9bfd;
  144. background: #eff6ff;
  145. padding: 2rpx 10rpx;
  146. border-radius: 4rpx;
  147. margin-right: 12rpx;
  148. }
  149. .f-version {
  150. font-size: 20rpx;
  151. color: #64748b;
  152. background: #f1f5f9;
  153. padding: 2rpx 10rpx;
  154. border-radius: 4rpx;
  155. margin-right: 12rpx;
  156. }
  157. .f-required {
  158. font-size: 20rpx;
  159. color: #ef4444;
  160. background: #fef2f2;
  161. padding: 2rpx 10rpx;
  162. border-radius: 4rpx;
  163. margin-right: 12rpx;
  164. }
  165. .f-time {
  166. font-size: 22rpx;
  167. color: #999;
  168. }
  169. }
  170. }
  171. .file-arrow {
  172. margin-left: 16rpx;
  173. flex-shrink: 0;
  174. }
  175. }
  176. }
  177. }
  178. .mt20 { margin-top: 20rpx; }
  179. </style>