index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <template>
  2. <view class="attr-content">
  3. <view class="a-t">
  4. <image class="image" mode="widthFix" :src="picture || product.picture"></image>
  5. <view class="right">
  6. <text class="title in2line">{{ product.name }}</text>
  7. <view class="sku-info-wrapper">
  8. <view class="price-wrapper">
  9. <image mode="aspectFit" class="image" v-if="product.memberDiscount && product.memberDiscount.length !== 0 && product.memberDiscount.discount > 0 && price > 0" :src="vipPrice"></image>
  10. <text :class="'text-' + themeColor.name" v-if="price">{{ moneySymbol }}{{ currentProductPrice }}</text>
  11. </view>
  12. <text class="stock" v-if="stock">库存:{{ stock }}{{ product.unit || '件' }}</text>
  13. <view class="selected in2line" v-if="specSelected.length > 0">
  14. 已选:
  15. <text
  16. class="selected-text"
  17. v-for="(sItem, sIndex) in specSelected"
  18. :key="sIndex"
  19. >
  20. {{ sItem.title }}
  21. </text>
  22. <text v-if="specSelected.length > 0"> * {{ cartCount }} </text>
  23. </view>
  24. </view>
  25. </view>
  26. </view>
  27. <scroll-view class="attr-content-scroll-view" scroll-y>
  28. <view v-for="(item, index) in specList" :key="index" class="attr-list">
  29. <text>{{ item.title }}</text>
  30. <view class="item-list">
  31. <view
  32. v-for="(childItem, childIndex) in specChildList"
  33. :key="childIndex"
  34. @tap="
  35. selectSpec(childIndex, childItem.base_spec_id, item.show_type)
  36. "
  37. >
  38. <view
  39. v-if="childItem.base_spec_id === item.base_spec_id"
  40. :class="[childItem.selected ? 'bg-' + themeColor.name : 'tit-normal', childItem.disabled ? 'disabled' : '']"
  41. :style="
  42. childItem.selected && parseInt(item.show_type) === 2
  43. ? styleObject
  44. : ''
  45. "
  46. class="tit"
  47. >
  48. <text v-if="parseInt(item.show_type) === 1">
  49. {{ childItem.title }}
  50. </text>
  51. <text v-if="parseInt(item.show_type) === 2">
  52. {{ childItem.title }}
  53. </text>
  54. <view v-if="parseInt(item.show_type) === 3">
  55. <image
  56. class="img"
  57. :src="childItem.data || product.picture"
  58. mode="aspectFill"
  59. ></image>
  60. {{ childItem.title }}
  61. </view>
  62. </view>
  63. </view>
  64. </view>
  65. </view>
  66. <view class="select-count" v-if="isSelectedNum">
  67. <text>购买数量</text>
  68. <!-- <rf-number-box-->
  69. <!-- v-if="parseInt(stock || product.stock, 10) === 0"-->
  70. <!-- class="step"-->
  71. <!-- :disabled="true"-->
  72. <!-- :min="0"-->
  73. <!-- :max="0"-->
  74. <!-- @eventChange="numberChange"-->
  75. <!-- ></rf-number-box>-->
  76. <rf-number-box
  77. class="step"
  78. :min="parseInt(product.min_buy, 10) || minNum"
  79. :max="parseInt(product.max_buy, 10) || (maxNum === 0 ? parseInt(stock || product.stock, 10) : maxNum)"
  80. :value="cartCount"
  81. @eventChange="numberChange"
  82. ></rf-number-box>
  83. <!-- :max="parseInt(stock || product.stock, 10)"-->
  84. </view>
  85. </scroll-view>
  86. <button v-if="!showBuyBtn" class="btn" :class="'bg-' + themeColor.name" @tap="toggleSpec">完成</button>
  87. <view class="btn-group" v-else>
  88. <button class="btn" :class="'bg-' + themeColor.name" @tap="toggleSpec(1)">加入购物车</button>
  89. <button class="btn" :class="'bg-' + themeColor.name" @tap="toggleSpec(2)">立即购买</button>
  90. </view>
  91. </view>
  92. </template>
  93. <script>
  94. /**
  95. *@des 礼品规范组件
  96. *@author stav stavyan@qq.com
  97. *@blog https://stavtop.club
  98. *@date 2020/05/03 19:17:15
  99. */
  100. import rfNumberBox from '@/components/rf-number-box';
  101. export default {
  102. name: 'rfAttrContent',
  103. components: { rfNumberBox },
  104. props: {
  105. showBuyBtn: {
  106. type: Boolean,
  107. default: false
  108. },
  109. isSelectedNum: {
  110. type: Boolean,
  111. default: true
  112. },
  113. type: {
  114. type: String,
  115. default: 'buy_now'
  116. },
  117. product: {
  118. type: Object,
  119. default() {
  120. return {};
  121. }
  122. },
  123. minNum: {
  124. type: Number,
  125. default: 1
  126. },
  127. maxNum: {
  128. type: Number,
  129. default: 0
  130. }
  131. },
  132. data() {
  133. return {
  134. styleObject: null,
  135. specList: [],
  136. specChildList: [],
  137. skuId: this.product && this.product.sku_id,
  138. price: null,
  139. stock: null,
  140. cartCount: parseInt(this.product.min_buy, 10) || this.minNum || 1,
  141. picture: null,
  142. specSelected: [],
  143. vipPrice: this.$mAssetsPath.vipPrice,
  144. moneySymbol: this.moneySymbol,
  145. skuName: null
  146. };
  147. },
  148. computed: {
  149. currentDiscountPrice() {
  150. const decimal = this.product.marketing.decimal_reservation_number;
  151. const discount = this.product.marketing.discount;
  152. const price = this.price;
  153. switch (parseInt(decimal, 10)) {
  154. case -1:
  155. return (price * discount / 100).toFixed(2);
  156. case 0:
  157. return (price * discount / 100).toFixed(0);
  158. case 1:
  159. return (price * discount / 100).toFixed(1);
  160. default:
  161. return (price * discount / 100).toFixed(2);
  162. }
  163. },
  164. currentProductPrice () {
  165. let price = this.price;
  166. if (this.type === 'discount') {
  167. price = this.currentSkuPrice || this.currentDiscountPrice;
  168. }
  169. if (this.type === 'group_buy') {
  170. price = this.currentSkuPrice || this.product.marketing.ladder.price;
  171. }
  172. if (this.product.memberDiscount && this.product.memberDiscount.length !== 0) {
  173. price = price * (1 - this.product.memberDiscount.discount / 100);
  174. }
  175. return parseFloat(price || '0').toFixed(2);
  176. }
  177. },
  178. async mounted() {
  179. await this.initData();
  180. },
  181. methods: {
  182. initData() {
  183. this.specList = this.product.base_attribute_format;
  184. this.specList.forEach(item => {
  185. this.specChildList = [...this.specChildList, ...item.value];
  186. // if (!this.product.sku_name) {
  187. // item.value[0].selected = true;
  188. // this.specSelected.push(item.value[0]);
  189. // }
  190. });
  191. if (this.product.sku_name) {
  192. this.specChildList.forEach(item => {
  193. if (this.product.sku_name.indexOf(item.title) !== -1) {
  194. item.selected = true;
  195. this.specSelected.push(item);
  196. }
  197. });
  198. }
  199. let skuStrArr = [];
  200. this.specSelected.forEach(item => {
  201. skuStrArr.push(item.base_spec_value_id);
  202. });
  203. this.product.sku.forEach(item => {
  204. if (item.data === skuStrArr.join('-')) {
  205. this.stock = item.stock;
  206. if (this.type === 'buy_now') {
  207. this.price = item.price;
  208. } else {
  209. this.price = this.product.marketing_type === 'wholesale' ? item.wholesale_price : item.price;
  210. }
  211. this.skuName = item.name;
  212. this.skuId = item.id;
  213. }
  214. });
  215. },
  216. numberChange(data) {
  217. this.cartCount = parseInt(data.number, 10);
  218. },
  219. // 选择规格
  220. selectSpec(index, pid, type) {
  221. let list = this.specChildList;
  222. list.forEach(item => {
  223. if (item.base_spec_id === pid) {
  224. this.$set(item, 'selected', false);
  225. }
  226. });
  227. if (parseInt(type, 10) === 3) {
  228. this.picture = list[index].data;
  229. }
  230. if (parseInt(type, 10) === 2) {
  231. this.styleObject = {
  232. color: list[index].data,
  233. border: `1px solid ${list[index].data}`
  234. };
  235. }
  236. this.$set(list[index], 'selected', true);
  237. // 存储已选择
  238. /**
  239. * 修复选择规格存储错误
  240. * 将这几行代码替换即可
  241. * 选择的规格存放在specSelected中
  242. */
  243. this.specSelected = [];
  244. list.forEach(item => {
  245. if (item.selected === true) {
  246. this.specSelected.push(item);
  247. }
  248. });
  249. let skuStr = [];
  250. this.specSelected.forEach(item => {
  251. skuStr.push(item.base_spec_value_id);
  252. });
  253. this.product.sku.forEach(item => {
  254. if (item.data === skuStr.join('-')) {
  255. this.picture = item.picture;
  256. this.stock = item.stock;
  257. this.price = this.product.marketing_type === 'wholesale' ? item.wholesale_price : item.price;
  258. this.skuId = item.id;
  259. this.skuName = item.name;
  260. }
  261. // else {
  262. // if (item.data.indexOf(skuStr.join('-')) !== -1) {
  263. // console.log('skuStr', skuStr);
  264. // console.log('item.data', item.data);
  265. // this.specChildList.forEach(item2 => {
  266. // if (item2.base_spec_value_id === '120') {
  267. // item2.disabled = true;
  268. // }
  269. // });
  270. // // console.log(this.specChildList);
  271. // }
  272. // }
  273. });
  274. },
  275. toggleSpec(type) {
  276. if (!this.skuId) {
  277. this.$mHelper.toast('请选择规格');
  278. return;
  279. }
  280. if (this.stock < 1) {
  281. this.$mHelper.toast('库存不足');
  282. return;
  283. }
  284. this.$emit('toggle', {
  285. stock: this.stock,
  286. skuId: this.skuId,
  287. cartCount: this.cartCount,
  288. skuName: this.skuName || this.singleSkuText,
  289. price: this.price,
  290. type: type
  291. });
  292. }
  293. }
  294. };
  295. </script>
  296. <style scoped lang="scss">
  297. .sku-info-wrapper {
  298. width: 100%;
  299. padding-bottom: $spacing-sm;
  300. }
  301. .price-wrapper {
  302. height: 38upx;
  303. display: flex;
  304. align-items: center;
  305. margin: $spacing-sm 0;
  306. .image {
  307. width: 120upx;
  308. height: 38upx;
  309. }
  310. .base-color {
  311. margin-top: 2upx;
  312. }
  313. }
  314. .btn-group {
  315. display: flex;
  316. justify-content: space-between;
  317. .btn {
  318. width: 40vw;
  319. }
  320. }
  321. </style>