app-attr.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. <template>
  2. <view class="app-attr">
  3. <view @click="alert">
  4. <slot name="button"></slot>
  5. </view>
  6. <view class="modal" v-if="display === 'block'" @click="close">
  7. <app-iphone-x>
  8. <view class="content" slot="empty-area" @tap.native.stop="preventD">
  9. <image src="../../../static/image/icon/close.png" class="close" @click="close"></image>
  10. <view class="first dir-left-nowrap">
  11. <view class="box-grow-0 img" @click="clickImg(attrPic)">
  12. <app-image :img-src="attrPic" width="100%" height="100%"></app-image>
  13. </view>
  14. <view class="info">
  15. <view class="dir-left-nowrap cross-center" :class="priceColor">
  16. <view class="dir-left-nowrap cross-center">
  17. <view class="dir-left-nowrap cross-center" v-if="selectAttr.extra || goods.extra">
  18. <view>{{selectAttr.extra ? selectAttr.extra.value + selectAttr.extra.name :
  19. goods.extra.value + goods.extra.name}}
  20. </view>
  21. <view v-if="!(goods.sign === 'integral_mall' && attrPrice == 0)">+</view>
  22. </view>
  23. <app-price v-if="!(goods.sign === 'integral_mall' && attrPrice == 0)" type="text-price-all" :price="attrPrice"
  24. :default-price="goods.price"></app-price>
  25. </view>
  26. <view v-if="goods.level_show === 1">
  27. <app-member-mark></app-member-mark>
  28. </view>
  29. </view>
  30. <!-- <view class="stock">库存:{{attrNum}}</view> -->
  31. <view class="stock">剩余名额:{{attrNum}}</view>
  32. </view>
  33. </view>
  34. <view class="second">
  35. <slot name="extra"></slot>
  36. <view class="attr-group" v-for="(item, index) in newAttrGroupList" :key="index">
  37. <view class="attr-group-name">{{item.attr_group_name}}</view>
  38. <view class="dir-left-wrap">
  39. <view v-for="(attr, key) in item.attr_list" :key="key" class="attr-item"
  40. :class="attr.checked ? theme + '-background active': 'attr-item-default' + (attr.attr_num_0 ? ' attr_num_0' : '')"
  41. @click="storeAttrClick(attr.attr_id, item.attr_group_id)">{{attr.attr_name}}
  42. </view>
  43. </view>
  44. </view>
  45. <!-- <view v-if="chooseNumber" class="dir-left-nowrap number-box cross-center">
  46. <view class="box-grow-1">数量</view>
  47. <view class="block box-grow-0 cross-center main-center" @click="numberSub"
  48. :class="number <= 1 ? 'disabled' : ''">-
  49. </view>
  50. <view class="number-input box-grow-0 cross-center main-center">
  51. <app-input type="number" v-model="number" :center="true" placeholder=" " @blur="numberBlur"
  52. :focus="false" width="88"></app-input>
  53. </view>
  54. <view class="block box-grow-0 cross-center main-center" @click="numberAdd">+</view>
  55. </view> -->
  56. </view>
  57. <view class="three dir-left-nowrap">
  58. <!-- <view class="box-grow-1 main-center cross-center" v-if="cartShow"
  59. :class="theme + '-secondary-background'" @click="cart">{{addText}}
  60. </view> -->
  61. <view v-if="is_show_buy" class="box-grow-1 main-center cross-center buy" :class="theme + '-background'" @click="buy">
  62. {{buyText}}
  63. </view>
  64. </view>
  65. </view>
  66. </app-iphone-x>
  67. </view>
  68. </view>
  69. </template>
  70. <script>
  71. import { mapState, mapGetters } from "vuex";
  72. import appMemberMark from "../../page-component/app-member-mark/app-member-mark.vue";
  73. import appPrice from "../../page-component/goods/app-price.vue";
  74. import appInput from "../../basic-component/app-input/app-input.vue";
  75. import appImage from "../../basic-component/app-image/app-image.vue";
  76. import appIphoneX from '../../basic-component/app-iphone-x/app-iphone-x.vue';
  77. export default {
  78. name: "app-attr",
  79. components: {
  80. appMemberMark,
  81. appPrice,
  82. appInput,
  83. 'app-image': appImage,
  84. 'app-iphone-x': appIphoneX,
  85. },
  86. props: {
  87. goods: Object,
  88. attrGroupList: Array,
  89. attrCart: {
  90. type: Array,
  91. default() {
  92. return [];
  93. }
  94. },
  95. cartShow: {
  96. type: Boolean,
  97. default() {
  98. return true
  99. }
  100. },
  101. previewUrl: String,
  102. submitUrl: String,
  103. goodsId: {
  104. type:Number,
  105. default() {
  106. return 0
  107. }
  108. },
  109. show: Number,
  110. buyText: {
  111. type: String,
  112. default() {
  113. return '立即报名';
  114. }
  115. },
  116. plugin: {
  117. default: '',
  118. },
  119. theme: {
  120. type: String,
  121. default: 'classic-red',
  122. },
  123. chooseNumber: {
  124. type: Boolean,
  125. default: true,
  126. },
  127. noPay: {
  128. type: Boolean,
  129. default: false,
  130. },
  131. buyClick: {
  132. type: Boolean,
  133. default: false,
  134. },
  135. addText: {
  136. type: String,
  137. default: '加入购物车',
  138. },
  139. is_show_buy: {
  140. type: Boolean,
  141. default: true,
  142. }
  143. },
  144. data() {
  145. return {
  146. display: 'none',
  147. number: 1,
  148. selectAttr: null,
  149. newAttrGroupList: null,
  150. pic_url: null,
  151. };
  152. },
  153. watch: {
  154. show() {
  155. this.alert();
  156. },
  157. newData: {
  158. handler() {
  159. this.$emit('attr', this.newData)
  160. },
  161. immediate: true,
  162. },
  163. attrGroupList: {
  164. handler() {
  165. this.newAttrGroupList = this.attrGroupList;
  166. if (this.display == 'block') {
  167. this.alert();
  168. }
  169. },
  170. immediate: true
  171. },
  172. goods: {
  173. handler() {
  174. if (this.display == 'block') {
  175. this.alert();
  176. }
  177. },
  178. immediate: true
  179. }
  180. },
  181. methods: {
  182. alert() {
  183. if (this.attrGroupList.length === 0) {
  184. return;
  185. }
  186. let attr_group_list = this.attrGroupList;
  187. let attrs = this.goods.attr;
  188. let select_attr = null;
  189. this.number = 1;
  190. if (attr_group_list.length === 1) {
  191. for (let i in attrs) {
  192. for (let j in attr_group_list[0].attr_list) {
  193. if (attr_group_list[0].attr_list[j].attr_id == attrs[i].attr_list[0].attr_id) {
  194. if (attrs[i].stock > 0) {
  195. if (attrs.length === 1) {
  196. attr_group_list[0].attr_list[j].checked = true;
  197. }
  198. attr_group_list[0].attr_list[j].attr_num_0 = false;
  199. this.pic_url = attr_group_list[0].attr_list[j].pic_url;
  200. } else {
  201. this.number = 0;
  202. attr_group_list[0].attr_list[j].checked = false;
  203. attr_group_list[0].attr_list[j].attr_num_0 = true;
  204. }
  205. }
  206. }
  207. }
  208. if (attrs.length === 1) {
  209. select_attr = attrs[0];
  210. this.$emit('attrtap', select_attr);
  211. }
  212. }
  213. this.display = 'block';
  214. this.newAttrGroupList = attr_group_list;
  215. this.selectAttr = select_attr;
  216. },
  217. close() {
  218. this.display = 'none';
  219. },
  220. preventD() {
  221. },
  222. storeAttrClick(attr_id, attr_group_id) {
  223. let attr_group_list = JSON.parse(JSON.stringify(this.newAttrGroupList));
  224. let attrs = this.goods.attr;
  225. let checkedAttr = [];
  226. let attr_cart = this.attrCart;
  227. for (let i in attr_group_list) {
  228. for (let j in attr_group_list[i].attr_list) {
  229. let temp = attr_group_list[i].attr_list[j];
  230. if (parseInt(attr_group_list[i].attr_group_id) == parseInt(attr_group_id)) {
  231. if (parseInt(temp.attr_id) === parseInt(attr_id)) {
  232. if (temp.checked) {
  233. temp.checked = false;
  234. } else {
  235. temp.checked = true;
  236. }
  237. if (temp.attr_num_0) {
  238. return;
  239. }
  240. } else {
  241. temp.checked = false;
  242. }
  243. }
  244. if (temp.checked) {
  245. if (i == 0) {
  246. this.pic_url = attr_group_list[0].attr_list[j].pic_url;
  247. }
  248. checkedAttr.push(attr_group_list[i].attr_group_id + '-' + temp.attr_id);
  249. }
  250. }
  251. }
  252. function inArray(val, arr) {
  253. return arr.some(function (v) {
  254. return val == v;
  255. })
  256. }
  257. let attrNum_0 = [];
  258. let select_attr = null;
  259. let number = 1;
  260. for (let i in attrs) {
  261. let arr = [];
  262. let sign = 0;
  263. for (let j in attrs[i].attr_list) {
  264. let param = attrs[i].attr_list[j].attr_group_id + '-' + attrs[i].attr_list[j].attr_id;
  265. if (!inArray(param, checkedAttr)) {
  266. sign += 1;
  267. arr.push(param);
  268. }
  269. }
  270. if (attrs[i].stock == 0 && sign <= 1) {
  271. attrNum_0 = attrNum_0.concat(arr);
  272. }
  273. if (sign == 0) {
  274. if (!select_attr) {
  275. select_attr = {};
  276. }
  277. select_attr = attrs[i];
  278. attr_cart.forEach(item => {
  279. if (item.attr_id == select_attr.id) {
  280. number = item.num;
  281. }
  282. });
  283. if (select_attr.stock <= 0) {
  284. uni.showToast({
  285. title: '库存不足',
  286. icon: 'none'
  287. });
  288. return;
  289. }
  290. if (select_attr.stock <= number) {
  291. number = select_attr.stock;
  292. }
  293. }
  294. }
  295. if (checkedAttr.length == 0) {
  296. select_attr = null;
  297. }
  298. //库存为0的规格添加标识
  299. for (let i in attr_group_list) {
  300. for (let j in attr_group_list[i].attr_list) {
  301. let cAttr = attr_group_list[i].attr_list[j];
  302. let cParam = attr_group_list[i].attr_group_id + '-' + cAttr.attr_id;
  303. if (inArray(cParam, attrNum_0) && !inArray(cParam, checkedAttr)) {
  304. cAttr.attr_num_0 = true;
  305. } else {
  306. cAttr.attr_num_0 = false;
  307. }
  308. }
  309. }
  310. this.newAttrGroupList = attr_group_list;
  311. this.selectAttr = select_attr;
  312. this.number = number;
  313. this.$emit('attrtap', this.selectAttr);
  314. },
  315. numberBlur(number) {
  316. number = parseInt(number.value);
  317. if (number > this.attrNum) {
  318. uni.showToast({
  319. title: '库存不足',
  320. icon: 'none'
  321. });
  322. number = this.attrNum;
  323. }
  324. this.$emit('attrtap', this.selectAttr);
  325. return this.number = number;
  326. },
  327. numberSub() {
  328. let number = this.number;
  329. if (number <= 1) {
  330. return true;
  331. }
  332. number--;
  333. this.number = number;
  334. this.$emit('attrtap', this.selectAttr);
  335. },
  336. numberAdd() {
  337. let number = this.number;
  338. number++;
  339. if (number > this.attrNum) {
  340. uni.showToast({
  341. title: '库存不足',
  342. icon: 'none'
  343. });
  344. this.number = this.attrNum;
  345. return;
  346. }
  347. this.number = number;
  348. this.$emit('attrtap', this.selectAttr);
  349. },
  350. cart() {
  351. if (!this.submit()) {
  352. return false;
  353. }
  354. let select_attr = this.selectAttr;
  355. if (this.goods.sign === 'pick') {
  356. this.$emit('add', select_attr, this.number);
  357. return;
  358. }
  359. if (this.goods.sign === 'miaosha') {
  360. this.$request({
  361. url: this.$api.miaosha.add_cart,
  362. data: {
  363. miaosha_goods_id: select_attr.goods_id,
  364. attr_id: select_attr.id,
  365. num: this.number
  366. },
  367. method: 'post'
  368. }).then(e => {
  369. uni.showToast({
  370. title: e.msg,
  371. type: 'success'
  372. });
  373. this.display = 'none';
  374. this.selectAttr.number = this.number;
  375. this.$emit('selectNumber', this.selectAttr);
  376. }).catch(e => {
  377. this.display = 'none';
  378. });
  379. } else {
  380. this.$request({
  381. url: this.$api.cart.add,
  382. data: {
  383. goods_id: select_attr.goods_id,
  384. attr: select_attr.id,
  385. num: this.number
  386. },
  387. method: 'post'
  388. }).then(e => {
  389. uni.showToast({
  390. title: e.msg,
  391. type: 'success'
  392. });
  393. this.display = 'none';
  394. this.selectAttr.number = this.number;
  395. this.$emit('selectNumber', this.selectAttr);
  396. }).catch(e => {
  397. this.display = 'none';
  398. });
  399. }
  400. },
  401. buy() {
  402. if (!this.submit()) return false;
  403. if (this.noPay) {
  404. this.$emit('pay', this.number);
  405. return;
  406. }
  407. if (this.buyClick) {
  408. this.display = 'none';
  409. this.selectAttr.number = this.number;
  410. this.$emit('buyClick', this.selectAttr);
  411. return false;
  412. }
  413. let goods = this.goods;
  414. console.log('goods---', goods);
  415. let number = this.number;
  416. let select_attr = this.selectAttr;
  417. let goods_attr_id = select_attr.id;
  418. let attr = [];
  419. for (let i in select_attr.attr_list) {
  420. attr.push({
  421. attr_id: select_attr.attr_list[i].attr_id,
  422. attr_group_id: select_attr.attr_list[i].attr_group_id
  423. });
  424. }
  425. let mch_list = [{
  426. mch_id: goods.mch_id ? goods.mch_id : 0,
  427. goods_list: [{
  428. id: this.goodsId ? this.goodsId: goods.id,
  429. attr: attr,
  430. num: number,
  431. cat_id: 0,
  432. goods_attr_id: goods_attr_id
  433. }]
  434. }];
  435. let page_url = `/pages/order-submit/order-submit?mch_list=${JSON.stringify(mch_list)}`;
  436. if (this.submitUrl && this.previewUrl) {
  437. page_url += `&preview_url=${encodeURIComponent(this.previewUrl)}&submit_url=${encodeURIComponent(this.submitUrl)}&plugin=${this.plugin}`;
  438. }
  439. uni.navigateTo({
  440. url: page_url
  441. })
  442. },
  443. submit() {
  444. let goods = this.goods;
  445. let number = this.number;
  446. let select_attr = this.selectAttr;
  447. if (!select_attr) {
  448. uni.showToast({
  449. title: '请先选规格',
  450. icon: 'none'
  451. });
  452. return false;
  453. }
  454. if (select_attr.stock <= 0) {
  455. uni.showToast({
  456. title: '库存不足',
  457. icon: 'none'
  458. });
  459. return false;
  460. }
  461. if (number <= 0) {
  462. uni.showToast({
  463. title: '数量不能为0',
  464. icon: 'none'
  465. });
  466. return false;
  467. }
  468. if (!goods) {
  469. return false;
  470. }
  471. return true;
  472. },
  473. clickImg(src) {
  474. uni.previewImage({
  475. current: 0,
  476. urls: [src]
  477. });
  478. },
  479. },
  480. computed: {
  481. ...mapState({
  482. gConfig: state => state.gConfig,
  483. }),
  484. attrPic() {
  485. if (this.pic_url) {
  486. return this.pic_url;
  487. } else {
  488. if (this.goods) {
  489. return this.goods.cover_pic;
  490. } else {
  491. return ``;
  492. }
  493. }
  494. },
  495. priceColor() {
  496. if (this.goods && this.goods.level_show === 1) {
  497. return `member`;
  498. } else {
  499. return this.theme + '-color';
  500. }
  501. },
  502. attrNum() {
  503. if (this.selectAttr) {
  504. return this.selectAttr.stock;
  505. } else {
  506. if (this.goods) {
  507. return this.goods.goods_num;
  508. } else {
  509. return 0;
  510. }
  511. }
  512. },
  513. attrPrice() {
  514. if (this.selectAttr) {
  515. if (this.goods.level_show === 1) {
  516. return this.selectAttr.price_member;
  517. } else {
  518. return this.selectAttr.price;
  519. }
  520. } else {
  521. if (this.goods) {
  522. return this.goods.price;
  523. } else {
  524. return 0;
  525. }
  526. }
  527. },
  528. newData() {
  529. const { number, display, selectAttr } = this;
  530. return {
  531. number,
  532. display,
  533. selectAttr
  534. }
  535. },
  536. ...mapGetters('iPhoneX', {
  537. boolEmpty: 'getBoolEmpty'
  538. })
  539. }
  540. }
  541. </script>
  542. <style scoped lang="scss">
  543. .app-attr {
  544. background-color: #ffffff;
  545. .modal {
  546. background-color: rgba(0, 0, 0, 0.5);
  547. position: fixed;
  548. top: 0;
  549. left: 0;
  550. width: 100%;
  551. height: 100%;
  552. z-index: 1601;
  553. .content {
  554. width: 100%;
  555. /*position: fixed;*/
  556. /*left: 0;*/
  557. /*bottom: 0;*/
  558. background-color: #ffffff;
  559. border-radius: #{16rpx} #{16rpx} 0 0;
  560. .close {
  561. width: #{30rpx};
  562. height: #{30rpx};
  563. position: absolute;
  564. right: #{24rpx};
  565. top: #{24rpx};
  566. background-color: #ffffff;
  567. }
  568. .first {
  569. margin: 0 #{24rpx};
  570. border-bottom: #{1rpx} solid #e2e2e2;
  571. .img {
  572. width: #{200rpx};
  573. height: #{200rpx};
  574. border: #{4rpx} solid #ffffff;
  575. border-radius: #{8rpx};
  576. margin-top: #{-64rpx};
  577. display: block;
  578. }
  579. .info {
  580. margin: #{36rpx} 0 #{26rpx} #{24rpx};
  581. font-size: $uni-font-size-import-two;
  582. line-height: 1;
  583. .stock {
  584. font-size: $uni-font-size-weak-one;
  585. color: $uni-general-color-two;
  586. margin-top: #{18rpx};
  587. }
  588. view {
  589. &:first-child {
  590. margin-right: #{12rpx};
  591. }
  592. }
  593. .member {
  594. color: #f39800;
  595. }
  596. }
  597. }
  598. .second {
  599. max-height: #{650rpx};
  600. overflow-y: auto;
  601. padding: #{4rpx} #{24rpx};
  602. font-size: $uni-font-size-general-two;
  603. .attr-group {
  604. padding: #{32rpx} 0;
  605. border-bottom: #{1rpx} solid #e2e2e2;
  606. .attr-group-name {
  607. color: $uni-general-color-one;
  608. margin-bottom: #{20rpx};
  609. }
  610. .attr-item {
  611. margin-right: #{20rpx};
  612. padding: #{15rpx 24rpx};
  613. border-radius: #{8rpx};
  614. margin-bottom: #{20rpx};
  615. &.attr-item-default {
  616. background-color: #f2f2f2;
  617. color: $uni-important-color-black;
  618. }
  619. &.active {
  620. color: #ffffff;
  621. }
  622. &.attr_num_0 {
  623. color: #cdcdcd;
  624. background-color: #f7f7f7;
  625. }
  626. }
  627. }
  628. .number-box {
  629. color: $uni-general-color-one;
  630. height: #{124rpx};
  631. .block {
  632. width: #{60rpx};
  633. height: #{60rpx};
  634. margin: 0 #{4rpx};
  635. background-color: $uni-weak-color-two;
  636. &.disabled {
  637. background-color: #fbfbfb;
  638. color: $uni-general-color-two;
  639. }
  640. }
  641. .number-input {
  642. width: #{88rpx};
  643. height: #{60rpx};
  644. color: $uni-important-color-black;
  645. font-size: $uni-font-size-general-one;
  646. background-color: $uni-weak-color-two;
  647. }
  648. }
  649. }
  650. .three {
  651. height: #{110rpx};
  652. width: 100%;
  653. font-size: $uni-font-size-general-one;
  654. }
  655. }
  656. }
  657. }
  658. .buy {
  659. color: #ffffff;
  660. }
  661. </style>