爱简收旧衣按件回收前端代码仓库
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.

395 lines
7.6 KiB

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