index.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <template>
  2. <div class="product-card" @click="onPath(`/item?id=${product.id}`)">
  3. <div class="card-header" v-if="showCheckbox || showAction">
  4. <el-checkbox v-if="showCheckbox" v-model="checked" @change="handleCheck" />
  5. <slot name="action">
  6. <span v-if="actionText" class="action-btn" @click="$emit('action')">{{ actionText }}</span>
  7. </slot>
  8. </div>
  9. <div class="product-image">
  10. <el-image :src="product.image" fit="contain">
  11. <template #error>
  12. <div class="image-placeholder">
  13. <el-icon :size="40" color="#ccc"><Picture /></el-icon>
  14. </div>
  15. </template>
  16. </el-image>
  17. </div>
  18. <div class="product-info">
  19. <div class="product-name">{{ product.name }}</div>
  20. <div class="product-price">
  21. <span v-if="product.tag" class="price-tag">{{ product.tag }}</span>
  22. <span class="current-price">¥{{ product.price }}</span>
  23. <span v-if="product.originalPrice" class="original-price">¥{{ product.originalPrice }}</span>
  24. <div v-if="showAddCart" class="add-cart" @click="$emit('addCart')">
  25. <el-icon><Plus /></el-icon>
  26. </div>
  27. </div>
  28. </div>
  29. </div>
  30. </template>
  31. <script setup lang="ts">
  32. import { ref, watch } from 'vue';
  33. import { Picture, Plus } from '@element-plus/icons-vue';
  34. import { onPath } from '@/utils/siteConfig';
  35. interface Product {
  36. id?: string | number;
  37. image?: string;
  38. name: string;
  39. price: string | number;
  40. originalPrice?: string | number;
  41. tag?: string;
  42. }
  43. const props = defineProps<{
  44. product: Product;
  45. modelValue?: boolean;
  46. showCheckbox?: boolean;
  47. showAction?: boolean;
  48. showAddCart?: boolean;
  49. actionText?: string;
  50. }>();
  51. const emit = defineEmits<{
  52. 'update:modelValue': [value: boolean];
  53. 'action': [];
  54. 'addCart': [];
  55. }>();
  56. const checked = ref(props.modelValue || false);
  57. watch(
  58. () => props.modelValue,
  59. (val) => {
  60. checked.value = val || false;
  61. }
  62. );
  63. const handleCheck = (val: boolean | string | number) => {
  64. emit('update:modelValue', !!val);
  65. };
  66. </script>
  67. <style scoped lang="scss">
  68. .product-card {
  69. background: #fff;
  70. border-radius: 8px;
  71. overflow: hidden;
  72. border: 1px solid #eee;
  73. .card-header {
  74. display: flex;
  75. justify-content: space-between;
  76. align-items: center;
  77. padding: 10px 12px;
  78. .action-btn {
  79. font-size: 12px;
  80. color: #999;
  81. cursor: pointer;
  82. &:hover {
  83. color: #e60012;
  84. }
  85. }
  86. }
  87. .product-image {
  88. height: 160px;
  89. background: #f9f9f9;
  90. display: flex;
  91. align-items: center;
  92. justify-content: center;
  93. padding: 10px;
  94. .el-image {
  95. width: 100%;
  96. height: 100%;
  97. }
  98. .image-placeholder {
  99. width: 100%;
  100. height: 100%;
  101. display: flex;
  102. align-items: center;
  103. justify-content: center;
  104. background: #f5f5f5;
  105. }
  106. }
  107. .product-info {
  108. padding: 12px;
  109. .product-name {
  110. font-size: 13px;
  111. color: #333;
  112. line-height: 1.4;
  113. height: 36px;
  114. overflow: hidden;
  115. display: -webkit-box;
  116. -webkit-line-clamp: 2;
  117. -webkit-box-orient: vertical;
  118. margin-bottom: 8px;
  119. }
  120. .product-price {
  121. display: flex;
  122. align-items: center;
  123. gap: 5px;
  124. position: relative;
  125. .price-tag {
  126. font-size: 12px;
  127. color: #e60012;
  128. }
  129. .current-price {
  130. font-size: 16px;
  131. font-weight: bold;
  132. color: #e60012;
  133. }
  134. .original-price {
  135. font-size: 12px;
  136. color: #999;
  137. text-decoration: line-through;
  138. }
  139. .add-cart {
  140. position: absolute;
  141. right: 0;
  142. bottom: 0;
  143. width: 24px;
  144. height: 24px;
  145. border-radius: 50%;
  146. border: 1px solid #e60012;
  147. color: #e60012;
  148. display: flex;
  149. align-items: center;
  150. justify-content: center;
  151. cursor: pointer;
  152. transition: all 0.2s;
  153. &:hover {
  154. background: #e60012;
  155. color: #fff;
  156. }
  157. }
  158. }
  159. }
  160. }
  161. </style>