爱简收旧衣按件回收前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

361 lines
7.1 KiB

2 weeks ago
  1. <template>
  2. <!-- 商品款式选择弹窗 -->
  3. <view v-if="showStylePopup" class="style-popup-mask">
  4. <view class="style-popup">
  5. <view class="style-popup-header">
  6. <text class="style-popup-close" @click="close">关闭</text>
  7. <text class="style-popup-title">选择回收款式</text>
  8. </view>
  9. <view class="style-popup-brand-info">
  10. <image :src="brandInfo.logo" class="brand-logo" mode="aspectFit" />
  11. <text class="brand-name">{{ brandInfo.name }}</text>
  12. </view>
  13. <scroll-view class="style-popup-list" scroll-y>
  14. <view class="style-grid">
  15. <view v-for="(item, index) in styleList" :key="item.id" class="style-item" @click="selectStyle(item, index)">
  16. <view class="style-item-content">
  17. <view class="image-container">
  18. <image :src="item.image" class="style-image" mode="aspectFill" />
  19. <view class="style-quantity">
  20. <button class="btn-minus" @click.stop="updateQuantity(index, -1)">-</button>
  21. <text class="quantity">{{ item.quantity || 0 }}</text>
  22. <button class="btn-plus" @click.stop="updateQuantity(index, 1)">+</button>
  23. </view>
  24. </view>
  25. <text class="style-name">{{ item.name }}</text>
  26. </view>
  27. </view>
  28. </view>
  29. </scroll-view>
  30. <view class="style-popup-footer">
  31. <button class="next-btn" @click="nextStep" :disabled="!hasSelectedItems">下一步</button>
  32. </view>
  33. </view>
  34. </view>
  35. </template>
  36. <script>
  37. export default {
  38. name: 'ProductStyleSelector',
  39. data() {
  40. return {
  41. showStylePopup: false,
  42. brandInfo: {
  43. id: '',
  44. name: '',
  45. logo: ''
  46. },
  47. styleList: [],
  48. currentProductId: null,
  49. existingQuantities: {}
  50. }
  51. },
  52. computed: {
  53. hasSelectedItems() {
  54. return this.styleList.some(item => (item.quantity || 0) > 0)
  55. }
  56. },
  57. methods: {
  58. // 打开款式选择弹窗
  59. open(brandInfo, productId, existingQuantities = {}) {
  60. if (!brandInfo || !productId) {
  61. console.error('brandInfo and productId are required')
  62. return
  63. }
  64. this.brandInfo = brandInfo
  65. this.currentProductId = productId
  66. this.existingQuantities = existingQuantities
  67. this.getProductStyles(brandInfo.id, productId)
  68. this.showStylePopup = true
  69. },
  70. // 关闭弹窗
  71. close() {
  72. this.showStylePopup = false
  73. this.styleList = []
  74. this.brandInfo = { id: '', name: '', logo: '' }
  75. this.currentProductId = null
  76. this.existingQuantities = {}
  77. this.$emit('close')
  78. },
  79. // 选择款式
  80. selectStyle(item, index) {
  81. // 点击款式项时的处理逻辑
  82. console.log('Selected style:', item)
  83. },
  84. // 更新数量
  85. updateQuantity(index, delta) {
  86. const item = this.styleList[index]
  87. if (!item) return
  88. let newQuantity = (item.quantity || 0) + delta
  89. if (newQuantity < 0) newQuantity = 0
  90. this.$set(item, 'quantity', newQuantity)
  91. },
  92. // 获取商品款式列表
  93. getProductStyles(brandId, productId) {
  94. const params = {
  95. brandId: brandId,
  96. productId: productId
  97. }
  98. this.$api('getGoodsBrandProduct', params, res => {
  99. if (res && res.success && res.result && res.result) {
  100. this.styleList = res.result.map(item => {
  101. const uniqueKey = `${brandId}_${item.id}`
  102. const existingQty = this.existingQuantities[uniqueKey] || 0
  103. return {
  104. id: item.id,
  105. name: item.name,
  106. image: item.image || '/static/default-product.png',
  107. minPrice: item.minPrice,
  108. maxPrice: item.maxPrice,
  109. brandId: item.brandId,
  110. shopId: item.shopId,
  111. quantity: existingQty,
  112. ...item
  113. }
  114. })
  115. } else {
  116. this.styleList = []
  117. }
  118. })
  119. },
  120. // 下一步
  121. nextStep() {
  122. const selectedItems = this.styleList.filter(item => (item.quantity || 0) > 0)
  123. if (selectedItems.length === 0) {
  124. uni.showToast({
  125. title: '请选择至少一个款式',
  126. icon: 'none'
  127. })
  128. return
  129. }
  130. this.$emit('style-confirm', {
  131. brandInfo: this.brandInfo,
  132. selectedStyles: selectedItems
  133. })
  134. this.close()
  135. }
  136. }
  137. }
  138. </script>
  139. <style lang="scss" scoped>
  140. .style-popup-mask {
  141. position: fixed;
  142. left: 0;
  143. right: 0;
  144. top: 0;
  145. bottom: 0;
  146. background: rgba(0,0,0,0.35);
  147. z-index: 3000;
  148. display: flex;
  149. align-items: flex-end;
  150. justify-content: center;
  151. }
  152. .style-popup {
  153. position: relative;
  154. width: 100%;
  155. max-width: 750px;
  156. background: #fff;
  157. border-radius: 32rpx 32rpx 0 0;
  158. box-shadow: 0 -4rpx 24rpx rgba(0,0,0,0.08);
  159. height: 94vh;
  160. display: flex;
  161. flex-direction: column;
  162. overflow: hidden;
  163. }
  164. .style-popup-header {
  165. display: flex;
  166. align-items: center;
  167. justify-content: center;
  168. padding: 32rpx 24rpx 0 24rpx;
  169. font-size: 32rpx;
  170. font-weight: bold;
  171. position: relative;
  172. }
  173. .style-popup-close {
  174. position: absolute;
  175. left: 24rpx;
  176. font-size: 28rpx;
  177. color: #888;
  178. }
  179. .style-popup-title {
  180. font-size: 32rpx;
  181. color: #222;
  182. font-weight: bold;
  183. }
  184. .style-popup-brand-info {
  185. display: flex;
  186. align-items: center;
  187. padding: 20rpx 24rpx;
  188. border-bottom: 1px solid #f0f0f0;
  189. }
  190. .brand-logo {
  191. width: 60rpx;
  192. height: 60rpx;
  193. border-radius: 8rpx;
  194. margin-right: 20rpx;
  195. background: #f8f8f8;
  196. }
  197. .brand-name {
  198. font-size: 28rpx;
  199. color: #222;
  200. font-weight: bold;
  201. }
  202. .style-popup-list {
  203. flex: 1;
  204. overflow-y: auto;
  205. padding: 0 24rpx;
  206. box-sizing: border-box;
  207. scrollbar-width: none;
  208. -ms-overflow-style: none;
  209. &::-webkit-scrollbar {
  210. width: 0 !important;
  211. display: none;
  212. }
  213. }
  214. .style-grid {
  215. display: flex;
  216. flex-wrap: wrap;
  217. gap: 20rpx;
  218. padding: 20rpx 0;
  219. }
  220. .style-item {
  221. width: calc((100% - 40rpx) / 3);
  222. display: flex;
  223. flex-direction: column;
  224. align-items: center;
  225. border-radius: 16rpx;
  226. background: #fff;
  227. box-sizing: border-box;
  228. }
  229. .style-item-content {
  230. display: flex;
  231. flex-direction: column;
  232. align-items: center;
  233. width: 100%;
  234. }
  235. .image-container {
  236. position: relative;
  237. width: 100%;
  238. height: 200rpx;
  239. margin-bottom: 16rpx;
  240. }
  241. .style-image {
  242. width: 100%;
  243. height: 100%;
  244. border-radius: 12rpx;
  245. background: #f8f8f8;
  246. }
  247. .style-name {
  248. font-size: 24rpx;
  249. color: #222;
  250. font-weight: 500;
  251. text-align: center;
  252. overflow: hidden;
  253. text-overflow: ellipsis;
  254. white-space: nowrap;
  255. width: 100%;
  256. }
  257. .style-quantity {
  258. position: absolute;
  259. bottom: 8rpx;
  260. left: 50%;
  261. transform: translateX(-50%);
  262. display: flex;
  263. align-items: center;
  264. justify-content: center;
  265. background: rgba(255, 255, 255, 0.9);
  266. border-radius: 24rpx;
  267. padding: 4rpx 8rpx;
  268. }
  269. .btn-minus, .btn-plus {
  270. width: 40rpx;
  271. height: 40rpx;
  272. border-radius: 50%;
  273. background: #fff;
  274. border: 1px solid #ddd;
  275. color: #333;
  276. font-size: 20rpx;
  277. display: flex;
  278. align-items: center;
  279. justify-content: center;
  280. margin: 0;
  281. padding: 0;
  282. box-shadow: 0 2rpx 4rpx rgba(0,0,0,0.1);
  283. &::after {
  284. border: none;
  285. }
  286. &:active {
  287. background: #f0f0f0;
  288. }
  289. }
  290. .quantity {
  291. width: 32rpx;
  292. text-align: center;
  293. font-size: 20rpx;
  294. color: #333;
  295. margin: 0 8rpx;
  296. font-weight: bold;
  297. }
  298. .style-popup-footer {
  299. padding: 20rpx 24rpx;
  300. border-top: 1px solid #f0f0f0;
  301. background: #fff;
  302. }
  303. .next-btn {
  304. width: 100%;
  305. height: 88rpx;
  306. background: linear-gradient(to right, #ffd01e, #ff8917);
  307. border-radius: 44rpx;
  308. color: #fff;
  309. font-size: 32rpx;
  310. font-weight: bold;
  311. border: none;
  312. display: flex;
  313. align-items: center;
  314. justify-content: center;
  315. &::after {
  316. border: none;
  317. }
  318. &:disabled {
  319. background: #ccc;
  320. color: #999;
  321. }
  322. &:active:not(:disabled) {
  323. opacity: 0.9;
  324. }
  325. }
  326. </style>