index.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <template>
  2. <section class="repair-image-panel">
  3. <div class="repair-image-panel__header">
  4. <div class="repair-image-panel__title">
  5. <el-icon><Camera /></el-icon>
  6. <span>维修前后图片</span>
  7. </div>
  8. <span class="repair-image-panel__total">共 {{ allImages.length }} 张</span>
  9. </div>
  10. <div class="repair-image-panel__body">
  11. <div class="repair-image-column">
  12. <div class="repair-image-column__header">
  13. <span>维修前</span>
  14. <el-tag size="small" type="info">{{ beforeImageList.length }} 张</el-tag>
  15. </div>
  16. <div v-if="beforeImageList.length" class="repair-image-list">
  17. <div
  18. v-for="(img, index) in beforeImageList"
  19. :key="`before-${index}-${img}`"
  20. class="repair-image-item"
  21. >
  22. <el-image
  23. class="repair-image-media"
  24. :src="img"
  25. :preview-src-list="beforeImageList"
  26. :initial-index="index"
  27. fit="cover"
  28. preview-teleported
  29. >
  30. <template #error>
  31. <div class="repair-image-error">图片加载失败</div>
  32. </template>
  33. </el-image>
  34. <span class="repair-image-item__index">{{ index + 1 }}</span>
  35. </div>
  36. </div>
  37. <div v-else class="repair-image-empty">暂无维修前图片</div>
  38. </div>
  39. <div class="repair-image-column">
  40. <div class="repair-image-column__header">
  41. <span>维修后</span>
  42. <el-tag size="small" type="success">{{ afterImageList.length }} 张</el-tag>
  43. </div>
  44. <div v-if="afterImageList.length" class="repair-image-list">
  45. <div
  46. v-for="(img, index) in afterImageList"
  47. :key="`after-${index}-${img}`"
  48. class="repair-image-item"
  49. >
  50. <el-image
  51. class="repair-image-media"
  52. :src="img"
  53. :preview-src-list="afterImageList"
  54. :initial-index="index"
  55. fit="cover"
  56. preview-teleported
  57. >
  58. <template #error>
  59. <div class="repair-image-error">图片加载失败</div>
  60. </template>
  61. </el-image>
  62. <span class="repair-image-item__index">{{ index + 1 }}</span>
  63. </div>
  64. </div>
  65. <div v-else class="repair-image-empty">暂无维修后图片</div>
  66. </div>
  67. </div>
  68. </section>
  69. </template>
  70. <script setup>
  71. import { computed } from 'vue'
  72. import { Camera } from '@element-plus/icons-vue'
  73. const props = defineProps({
  74. beforeImages: {
  75. type: [String, Array, Object],
  76. default: ''
  77. },
  78. afterImages: {
  79. type: [String, Array, Object],
  80. default: ''
  81. }
  82. })
  83. const baseUrl = import.meta.env.VITE_APP_BASE_API
  84. function normalizeImageUrl(value) {
  85. const url = String(value || '').trim()
  86. if (!url || /^(https?:|blob:|data:)/.test(url)) return url
  87. if (baseUrl && (url === baseUrl || url.startsWith(`${baseUrl}/`))) return url
  88. return url.startsWith('/') ? baseUrl + url : `${baseUrl}/${url}`
  89. }
  90. function toImageList(value) {
  91. const values = Array.isArray(value)
  92. ? value
  93. : typeof value === 'string'
  94. ? value.split(',')
  95. : value
  96. ? [value]
  97. : []
  98. return values
  99. .map(item => typeof item === 'string' ? item : (item.url || item.fileName || item.name))
  100. .map(normalizeImageUrl)
  101. .filter(Boolean)
  102. }
  103. const beforeImageList = computed(() => toImageList(props.beforeImages))
  104. const afterImageList = computed(() => toImageList(props.afterImages))
  105. const allImages = computed(() => beforeImageList.value.concat(afterImageList.value))
  106. </script>
  107. <style scoped lang="scss">
  108. .repair-image-panel {
  109. margin-top: 20px;
  110. padding-top: 16px;
  111. border-top: 1px solid #e9ecef;
  112. }
  113. .repair-image-panel__header,
  114. .repair-image-column__header {
  115. display: flex;
  116. align-items: center;
  117. justify-content: space-between;
  118. }
  119. .repair-image-panel__header {
  120. margin-bottom: 12px;
  121. }
  122. .repair-image-panel__title {
  123. display: inline-flex;
  124. align-items: center;
  125. gap: 8px;
  126. color: #303133;
  127. font-size: 16px;
  128. font-weight: 600;
  129. }
  130. .repair-image-panel__total {
  131. color: #909399;
  132. font-size: 13px;
  133. }
  134. .repair-image-panel__body {
  135. display: grid;
  136. grid-template-columns: repeat(2, minmax(0, 1fr));
  137. gap: 16px;
  138. }
  139. .repair-image-column {
  140. min-width: 0;
  141. padding: 14px;
  142. border: 1px solid #ebeef5;
  143. border-radius: 8px;
  144. background: #fafbfc;
  145. }
  146. .repair-image-column__header {
  147. margin-bottom: 12px;
  148. color: #606266;
  149. font-size: 14px;
  150. font-weight: 600;
  151. }
  152. .repair-image-list {
  153. display: grid;
  154. grid-template-columns: repeat(auto-fill, minmax(116px, 1fr));
  155. gap: 10px;
  156. }
  157. .repair-image-item {
  158. position: relative;
  159. width: 100%;
  160. aspect-ratio: 4 / 3;
  161. overflow: hidden;
  162. border: 1px solid #dcdfe6;
  163. border-radius: 6px;
  164. background: #f2f6fc;
  165. cursor: pointer;
  166. }
  167. .repair-image-item:hover :deep(.el-image__inner) {
  168. transform: scale(1.04);
  169. }
  170. .repair-image-media {
  171. width: 100%;
  172. height: 100%;
  173. }
  174. .repair-image-media :deep(.el-image__inner) {
  175. transition: transform 0.2s ease;
  176. }
  177. .repair-image-item__index {
  178. position: absolute;
  179. right: 5px;
  180. bottom: 5px;
  181. min-width: 20px;
  182. padding: 2px 5px;
  183. border-radius: 10px;
  184. background: rgb(0 0 0 / 55%);
  185. color: #fff;
  186. font-size: 12px;
  187. line-height: 16px;
  188. text-align: center;
  189. }
  190. .repair-image-empty,
  191. .repair-image-error {
  192. display: flex;
  193. align-items: center;
  194. justify-content: center;
  195. color: #a8abb2;
  196. font-size: 13px;
  197. }
  198. .repair-image-empty {
  199. min-height: 120px;
  200. border: 1px dashed #dcdfe6;
  201. border-radius: 6px;
  202. }
  203. .repair-image-error {
  204. width: 100%;
  205. height: 100%;
  206. }
  207. @media (max-width: 900px) {
  208. .repair-image-panel__body {
  209. grid-template-columns: 1fr;
  210. }
  211. }
  212. </style>