u-attr.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. <template>
  2. <view class="u-attr">
  3. <view @click="turnOn">
  4. <slot name="btn"></slot>
  5. </view>
  6. <u-popup v-model="newValue" mode="bottom" border-radius="14" :safeAreaInsetBottom="true" @close="close">
  7. <view class="u-model" @touchmove.stop.prevent>
  8. <view class="u-top dir-left-nowrap u-border-box">
  9. <view class="u-pic u-border-box" @click="clickImg(imgUrl)">
  10. <image class="u-img" :src="imgUrl"></image>
  11. </view>
  12. <view class="u-info">
  13. <view :class="['cross-center', 'dir-left-nowrap', themeObject.color]">
  14. <view class="dir-left-nowrap">
  15. <slot name="priceBefore"></slot>
  16. <view class="u-price">
  17. <app-price
  18. v-if="is_show_price"
  19. :theme="themeObject.theme"
  20. :userTheme="themeObject.userTheme"
  21. :sign="sign"
  22. :price="sellPrice"
  23. :default-price="goods.price"
  24. ></app-price>
  25. </view>
  26. </view>
  27. <app-member-mark
  28. v-if="goods.level_show === 1"
  29. :theme="themeObject"
  30. ></app-member-mark>
  31. </view>
  32. <view class="u-stock">库存:{{stock}}</view>
  33. </view>
  34. <view class="u-close-image" @click="close">
  35. <image class="bd-close-image u-border-box" src="/static/image/icon/icon-close.png" ></image>
  36. </view>
  37. </view>
  38. <view class="u-center">
  39. <scroll-view class="u-scroll-view" scroll-y="true">
  40. <slot name="extra"></slot>
  41. <view v-if="goods.type === 'goods'" class="u-attr-group u-border-box" v-for="(item, index) in newGroup" :key="index">
  42. <view class="u-group-name u-text">{{item.attr_group_name}}</view>
  43. <view class="dir-left-wrap" >
  44. <view :class="['u-group-item', attr.select ? 'u-checked ' + themeObject.back : 'u-unchecked', attr.num_0 ? 'u-attr_num_0' : '']"
  45. @click="storeAttr(attr.attr_id, item.attr_group_id, attr.num_0)"
  46. v-for="(attr, key) in item.attr_list" :key="key">
  47. {{attr.attr_name}}
  48. </view>
  49. </view>
  50. </view>
  51. <view class="service" v-if="service_screen&&service_screen.screenList">
  52. <view class="service_title">服务</view>
  53. <view class="service_item">
  54. <view>碎屏险</view>
  55. <view>{{service_screen.screenList.intro}}</view>
  56. </view>
  57. <view class="service_value">
  58. <view class="service_block"
  59. v-for="(item,index) of service_screen.screenList['card_data']"
  60. :class="{service_block_active:index == serviceId}"
  61. @click="select_service(index)">
  62. {{item.name}}{{item.year}}年{{item.price}}元
  63. </view>
  64. </view>
  65. </view>
  66. <view class="u-number dir-left-nowrap main-between cross-center">
  67. <view class="u-text">数量</view>
  68. <view class="dir-left-nowrap u-input-box">
  69. <view @click.stop="numberSub" :class="[number <=1 ? 'u-reduced-1' : 'u-reduced-0', 'u-number-btn']"></view>
  70. <input @blur="numberBlur" type="number" class="u-input" v-model="number">
  71. <view @click.stop="numberAdd" class="u-number-btn u-added-1"></view>
  72. </view>
  73. </view>
  74. </scroll-view>
  75. </view>
  76. <view class="u-bottom dir-left-nowrap">
  77. <view
  78. v-if="is_show_left && (goods.type === 'goods' || is_must_left)"
  79. class="box-grow-1"
  80. :class="cartClass"
  81. @click="leftSubmit">
  82. <slot name="left_slot"></slot>
  83. <template v-if="!$slots.left_slot">{{leftText}}</template>
  84. </view>
  85. <view
  86. v-if="is_show_right"
  87. class="box-grow-1"
  88. @click="rightSubmit"
  89. :class="[!$slots.right_slot ? themeObject.back + ' u-btn u-btn-color' : '']"
  90. >
  91. <slot name="right_slot"></slot>
  92. <template v-if="!$slots.right_slot">{{rightText}}</template>
  93. </view>
  94. <template v-if="$slots.right">
  95. <slot name="right"></slot>
  96. </template>
  97. </view>
  98. </view>
  99. </u-popup>
  100. </view>
  101. </template>
  102. <script>
  103. import uPopup from '../../basic-component/u-popup/u-popup.vue';
  104. import appPrice from "../goods/app-price.vue";
  105. import appMemberMark from "../app-member-mark/app-member-mark.vue";
  106. export default {
  107. name: "u-attr",
  108. props: {
  109. value: {
  110. type: Boolean
  111. },
  112. goods: {
  113. type: Object
  114. },
  115. themeObject: {
  116. type: Object
  117. },
  118. checked: {
  119. type: Object
  120. },
  121. is_show_price: {
  122. type: Boolean,
  123. default: true
  124. },
  125. is_show_left: {
  126. type: Boolean,
  127. default: true
  128. },
  129. is_must_left: {
  130. type: Boolean,
  131. default: true
  132. },
  133. is_show_right: {
  134. type: Boolean,
  135. default: true
  136. },
  137. leftText: {
  138. type: String,
  139. default: '加入购物车'
  140. },
  141. rightText: {
  142. type: String,
  143. default: '立即购买'
  144. },
  145. leftFunc: {
  146. type: Boolean
  147. },
  148. rightFunc: {
  149. type: Boolean
  150. },
  151. sign: {
  152. type: String
  153. },
  154. again: {
  155. type: Number
  156. }
  157. },
  158. data() {
  159. return {
  160. newValue: false,
  161. picUrl: null,
  162. newGroup: [],
  163. number: 1,
  164. service_screen:null, //碎屏险
  165. service_label:null, //当前对屏对象
  166. serviceId:null, //碎屏险ID
  167. card_id:null
  168. }
  169. },
  170. methods: {
  171. select_service(index){
  172. if(this.serviceId && this.serviceId == index){
  173. this.serviceId = null
  174. }else{
  175. this.serviceId = index
  176. }
  177. if(this.serviceId || this.serviceId===0){
  178. this.service_screen.screenList['card_data'].forEach((item,index)=>{
  179. console.log(index)
  180. console.log(this.serviceId)
  181. if(index == this.serviceId){
  182. this.service_label = item
  183. this.card_id = this.service_screen.screenList['card_id']
  184. }
  185. })
  186. }else{
  187. this.service_label = null
  188. }
  189. },
  190. getservice(copyGroup, attrNum_0, select){
  191. console.log('getservice,164')
  192. console.log(copyGroup, attrNum_0, select)
  193. console.log(this.goods.attr)
  194. console.log(select)
  195. let select_val = []
  196. this.goods.attr.forEach((item,index)=>{
  197. console.log(item)
  198. let select_item_list = []
  199. let select_item_value = ''
  200. item.attr_list.forEach((value,key)=>{
  201. select_item_value = value.attr_group_id+'-'+value.attr_id
  202. select_item_list.push(select_item_value)
  203. })
  204. if(JSON.stringify(select) == JSON.stringify(select_item_list)){
  205. this.service_screen = item
  206. }
  207. })
  208. },
  209. close: function() {
  210. this.$emit('input', false);
  211. },
  212. turnOn: function() {
  213. this.$emit('input', true);
  214. },
  215. inArray: function(newVal, arr) {
  216. return arr.some(v => {
  217. return newVal === v;
  218. });
  219. },
  220. identifier: function(copyGroup, attrNum_0, select) {
  221. console.log('identifier,175')
  222. copyGroup.forEach(f => {
  223. f.attr_list.forEach(c => {
  224. let param = `${f.attr_group_id}-${c.attr_id}`;
  225. this.inArray(param, attrNum_0) && !this.inArray(param, select) ? c.num_0 = true : c.num_0 = false;
  226. });
  227. });
  228. },
  229. selectCheck: function(copyAttr, attrNum_0, select) {
  230. console.log('selectCheck,183')
  231. copyAttr.forEach(f => {
  232. let arr = [];
  233. let sign = 0;
  234. f.attr_list.forEach(c => {
  235. let param = `${c.attr_group_id}-${c.attr_id}`;
  236. if (!this.inArray(param, select)) {
  237. sign += 1;
  238. arr.push(param);
  239. }
  240. });
  241. if (f.stock === 0 && sign <= 1) Array.prototype.push.apply(attrNum_0, arr);
  242. if (sign === 0) {
  243. this.$emit('check', f, 1);
  244. }
  245. });
  246. },
  247. storeAttr: function(attr_id, group_id, num_0) {
  248. console.log(201)
  249. if (num_0 === true) return;
  250. let newGroup = this.newGroup;
  251. let copyAttr = this.copyAttr;
  252. let select = [];
  253. console.log(newGroup)
  254. console.log(attr_id)
  255. console.log(group_id)
  256. newGroup.forEach((i, index) => {
  257. let attr_list = i.attr_list;
  258. attr_list.forEach((k) => {
  259. if (i.attr_group_id === group_id) {
  260. if (k.attr_id === attr_id) {
  261. if (k.select === true) {
  262. k.select = false;
  263. } else {
  264. k.select = true;
  265. }
  266. } else {
  267. k.select = false;
  268. }
  269. // k.attr_id === attr_id ? k.select = true : k.select = false;
  270. }
  271. if (k.select === true) {
  272. select.push(`${i.attr_group_id}-${k.attr_id}`);
  273. if (index === 0) this.picUrl = k.pic_url;
  274. }
  275. });
  276. });
  277. let attrNum_0 = [];
  278. console.log(230)
  279. console.log(copyAttr)
  280. console.log(attrNum_0)
  281. console.log(select)
  282. this.selectCheck(copyAttr, attrNum_0, select);
  283. this.$nextTick(() => {
  284. if (this.number > this.stock) this.number = this.stock;
  285. });
  286. this.identifier(newGroup, attrNum_0, select);
  287. if (select.length !== newGroup.length) {
  288. this.$emit('check', null, 1);
  289. }
  290. },
  291. firstSelect: function() {
  292. if (!this.copyGroup || !this.copyAttr) return;
  293. let copyGroup = this.copyGroup;
  294. let copyAttr = this.copyAttr;
  295. let groupLength = copyGroup.length;
  296. let select = [];
  297. copyAttr.forEach(i => {
  298. let attr_list = i.attr_list;
  299. attr_list.forEach(j => {
  300. let attr_group_id = j.attr_group_id;
  301. copyGroup.forEach(k => {
  302. if (attr_group_id === k.attr_group_id) {
  303. let groups_attr_list = k.attr_list;
  304. groups_attr_list.forEach(g => {
  305. if (g.attr_id === j.attr_id) {
  306. if (i.stock > 0) {
  307. if (groupLength > 0) {
  308. g.select = true;
  309. select.push(`${k.attr_group_id}-${g.attr_id}`);
  310. if (groupLength === copyGroup.length) {
  311. this.picUrl = g.pic_url;
  312. }
  313. groupLength--;
  314. }
  315. }
  316. }
  317. });
  318. }
  319. });
  320. });
  321. });
  322. let attrNum_0 = [];
  323. this.selectCheck(copyAttr, attrNum_0, select);
  324. this.identifier(copyGroup, attrNum_0, select);
  325. this.getservice(copyGroup, attrNum_0, select); //getservice
  326. this.newGroup = copyGroup;
  327. },
  328. numberBlur: function (e) {
  329. let value = parseInt(e.detail.value);
  330. if (!value) value = 1;
  331. if (value > this.stock) {
  332. value = this.stock;
  333. uni.showToast({
  334. title: '库存不足',
  335. icon: 'none'
  336. });
  337. }
  338. this.number = value;
  339. },
  340. numberSub:function() {
  341. let value = this.number;
  342. if (value > 1) {
  343. value--;
  344. this.number = value;
  345. }
  346. },
  347. numberAdd: function() {
  348. let value = this.number;
  349. value++;
  350. if (value > this.stock) {
  351. value = this.stock;
  352. uni.showToast({
  353. title: '库存不足',
  354. icon: 'none'
  355. });
  356. }
  357. this.number = value;
  358. },
  359. leftSubmit:function() {
  360. if (this.leftFunc === true) {
  361. this.$emit('leftFunc', this.number);
  362. } else {
  363. this.cart();
  364. }
  365. this.close();
  366. },
  367. rightSubmit:function() {
  368. let _this = this
  369. if (!this.checked) {
  370. uni.showToast({
  371. title: '请先选规格',
  372. icon: 'none'
  373. });
  374. return false;
  375. }
  376. // if (!this.service_label) {
  377. // uni.showModal({
  378. // title: '提示',
  379. // content: '确定不需要碎屏险吗?',
  380. // success: function (res) {
  381. // if (res.confirm) {
  382. // _this.submit()
  383. // } else if (res.cancel) {
  384. // return false
  385. // }
  386. // }
  387. // });
  388. // }else{
  389. // this.submit()
  390. // }
  391. this.submit()
  392. },
  393. submit(){
  394. let attrs = [];
  395. let _this = this
  396. _this.checked.attr_list.forEach(item => {
  397. attrs.push({
  398. attr_id: item.attr_id,
  399. attr_group_id: item.attr_group_id
  400. });
  401. });
  402. let goods = {
  403. mch_id: _this.goods.mch_id ? _this.goods.mch_id : 0,
  404. goods_list: [
  405. {
  406. id: _this.goods.id,
  407. attrs,
  408. num: _this.number,
  409. cat_id: 0,
  410. card_id:_this.card_id,
  411. goods_attr_id: _this.checked.id,
  412. service_label: _this.service_label
  413. }
  414. ]
  415. }
  416. if (_this.rightFunc === true) {
  417. console.log(333)
  418. _this.$emit('rightFunc', goods);
  419. } else {
  420. console.log(336)
  421. _this.shop(goods);
  422. }
  423. _this.close();
  424. },
  425. shop: function(goods) {
  426. console.log(342)
  427. console.log(goods)
  428. uni.navigateTo({
  429. url: `/pages/order-submit/order-submit?mch_list=${JSON.stringify([goods])}`
  430. });
  431. },
  432. cart: function() {
  433. if (!this.checked) {
  434. uni.showToast({
  435. title: '请先选规格',
  436. icon: 'none'
  437. });
  438. return false;
  439. }
  440. if (this.goods.type === 'ecard') return;
  441. this.$request({
  442. url: this.$api.cart.add,
  443. method: 'post',
  444. data: {
  445. goods_id: this.checked.goods_id,
  446. attr: this.checked.id,
  447. num: this.number
  448. }
  449. }).then(res => {
  450. if (res.code === 0) {
  451. uni.showToast({
  452. title: res.msg,
  453. icon: 'none'
  454. });
  455. this.$emit('cart', this.checked, this.number);
  456. this.close();
  457. } else {
  458. uni.showToast({
  459. title: res.msg,
  460. icon: "none",
  461. duration: 2500
  462. });
  463. }
  464. });
  465. },
  466. clickImg(src) {
  467. uni.previewImage({
  468. current: 0,
  469. urls: [src]
  470. });
  471. }
  472. },
  473. components: {
  474. uPopup,
  475. appPrice,
  476. appMemberMark
  477. },
  478. computed: {
  479. imgUrl: function() {
  480. if (this.picUrl) {
  481. return this.picUrl;
  482. } else if (this.goods) {
  483. return this.goods.cover_pic;
  484. } else {
  485. return '';
  486. }
  487. },
  488. stock: function() {
  489. if (!this.$validation.isEmpty(this.checked)) {
  490. return this.checked.stock;
  491. } else if (!this.$validation.isEmpty(this.goods)) {
  492. return this.goods.goods_num;
  493. }
  494. },
  495. sellPrice: function() {
  496. if (!this.$validation.isEmpty(this.checked)) {
  497. return this.goods.level_show === 1 ? this.checked.price_member : this.checked.price;
  498. } else if (!this.$validation.isEmpty(this.goods)) {
  499. return this.goods.hasOwnProperty('price_min') ? this.goods.price_min : this.goods.price;
  500. }
  501. },
  502. copyGroup: function() {
  503. if (!this.goods) return;
  504. let group = this.$utils.deepClone(this.goods.attr_groups);
  505. for (let i = 0; i < group.length; i++) {
  506. group[i].attr_list.forEach(item => {
  507. item.select = false;
  508. item.num_0 = false;
  509. });
  510. }
  511. return group;
  512. },
  513. copyAttr: function() {
  514. if (!this.goods) return;
  515. return this.$utils.deepClone(this.goods.attr);
  516. },
  517. cartClass: function() {
  518. if (!this.$slots.left_slot) {
  519. if (this.themeObject.theme === 'a' || this.themeObject.theme === 'b' || this.themeObject.theme === 'f') {
  520. return this.themeObject.sBack + ' u-btn u-btn-color ';
  521. } else {
  522. return this.themeObject.sBack + ' u-btn ' + this.themeObject.color;
  523. }
  524. } else {
  525. return '';
  526. }
  527. }
  528. },
  529. watch: {
  530. value: {
  531. handler(newVal) {
  532. this.newValue = newVal;
  533. if (newVal === false) return;
  534. if (this.$validation.isEmpty(this.checked)) this.$utils.throttle(this.firstSelect, 800);
  535. },
  536. immediate: true
  537. },
  538. number: {
  539. handler(newVal) {
  540. this.$emit('check', this.checked, newVal);
  541. }
  542. },
  543. again: {
  544. handler() {
  545. this.firstSelect();
  546. }
  547. }
  548. }
  549. }
  550. </script>
  551. <style scoped >
  552. .u-model {
  553. width: 750upx;
  554. }
  555. .u-top {
  556. margin: 0 24upx;
  557. border-bottom: 1upx solid #e2e2e2;
  558. height: 140upx;
  559. }
  560. .u-close-image {
  561. width: 54upx;
  562. height: 78upx;
  563. padding: 24upx 0 24rpx 24rpx;
  564. margin-left: 24rpx;
  565. }
  566. .bd-close-image {
  567. width: 30upx;
  568. height: 30upx;
  569. }
  570. .u-pic {
  571. width: 200rpx;
  572. height: 200rpx;
  573. padding: 4upx;
  574. border-radius: 8rpx;
  575. position: relative;
  576. top: -64upx;
  577. background-color: #ffffff;
  578. }
  579. .u-img {
  580. width: 192rpx;
  581. height: 192rpx;
  582. background-color: #ffffff;
  583. }
  584. .u-info {
  585. width: 424upx;
  586. height: 136upx;
  587. padding: 45upx 0 0 24upx;
  588. }
  589. .u-scroll-view {
  590. width: 100%;
  591. max-height: calc(80vh - 154upx);
  592. }
  593. .u-attr-group {
  594. margin:24upx 32upx 0 32upx;
  595. border-bottom: 1upx solid #e2e2e2;
  596. }
  597. .u-number {
  598. height: 124upx;
  599. margin: 0 32upx;
  600. }
  601. .u-group-name {
  602. margin-bottom: 20upx;
  603. }
  604. .u-group-item {
  605. padding: 15upx 24upx;
  606. border-radius: 8upx;
  607. margin:0 20upx 20upx 0;
  608. font-size: 26upx;
  609. }
  610. .u-checked {
  611. color: #ffffff;
  612. }
  613. .u-unchecked {
  614. background-color: #f2f2f2;
  615. color: #353535;
  616. }
  617. .u-attr_num_0 {
  618. background-color: #f7f7f7;
  619. color: #cdcdcd;
  620. }
  621. .u-stock {
  622. font-size: 24upx;
  623. color: #999999;
  624. }
  625. .u-price {
  626. margin-right: 12upx;
  627. }
  628. .u-input {
  629. width: 88upx;
  630. height: 60upx;
  631. background-color: #f7f7f7;
  632. font-size: 20upx;
  633. color: #353535;
  634. text-align: center;
  635. }
  636. .u-input-box {
  637. width: 218upx;
  638. }
  639. .u-number-btn {
  640. height: 60upx;
  641. width: 60upx;
  642. background-repeat: no-repeat;
  643. background-size: 100% 100%;
  644. background-position: center;
  645. }
  646. .u-number-btn:first-child {
  647. margin-right: 5upx;
  648. }
  649. .u-number-btn:last-child {
  650. margin-left: 5upx;
  651. }
  652. .u-reduced-1 {
  653. background-image: url("../../../static/image/icon/can-be-reduced.png");
  654. }
  655. .u-reduced-0 {
  656. background-image: url("../../../static/image/cart/can-be-reduced.png");
  657. }
  658. .u-added-1 {
  659. background-image: url("../../../static/image/cart/can-be-added.png");
  660. }
  661. .u-text {
  662. font-size: 26upx;
  663. color: #666666;
  664. }
  665. .u-bottom {
  666. height: 110upx;
  667. }
  668. .u-btn {
  669. text-align: center;
  670. line-height: 110upx;
  671. }
  672. .u-btn-color {
  673. color: #ffffff;
  674. }
  675. .service_title{
  676. padding-top: 10px;
  677. margin:14px 10px 10px;
  678. font-weight: 700;
  679. font-size: 13px;
  680. }
  681. .service_item{
  682. margin:14px 10px 0px;
  683. font-size:26rpx;
  684. color:#666;
  685. display:flex;
  686. justify-content: space-between;
  687. }
  688. .service_value{
  689. display:flex;
  690. flex-wrap: wrap;
  691. padding:0px 10px;
  692. }
  693. .service_block{
  694. min-width:46%;
  695. height: 28px;
  696. line-height:26px;
  697. border: 1px solid #f5f5f5;
  698. border-radius: 16px;
  699. text-align:center;
  700. margin-right: 10px;
  701. margin-top: 10px;
  702. font-size: 12px;
  703. background-color: #f5f5f5;
  704. position: relative;
  705. }
  706. .service_block_active{
  707. color:#f21c1c;
  708. border:1px solid #f21c1c;
  709. }
  710. </style>