index.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. <template>
  2. <view class="goods-sku-popup popup" catchtouchmove="true" :class="(value && complete) ? 'show' : 'none'"
  3. @touchmove.stop.prevent="moveHandle">
  4. <!-- 页面内容开始 -->
  5. <view class="mask" @click="close('mask')"></view>
  6. <!-- 页面开始 -->
  7. <view class="layer attr-content" :style="'border-radius: '+borderRadius+'rpx '+borderRadius+'rpx 0 0;'">
  8. <view class="specification-wrapper">
  9. <scroll-view class="specification-wrapper-content" scroll-y="true">
  10. <view class="specification-header">
  11. <view class="specification-left">
  12. <image class="product-img" :src="selectShop.image ? selectShop.image : goodsInfo[goodsThumbName]"
  13. mode="aspectFill"></image>
  14. </view>
  15. <view class="specification-right">
  16. <view class="price-content" :style="'color: '+priceColor+' ;'">
  17. <text class="sign">¥</text>
  18. <text class="price">{{ (selectShop.price || defaultPrice) | priceFilter }}</text>
  19. </view>
  20. <view class="inventory">{{ stockText }}:{{ selectShop[stockName] || defaultStock }}</view>
  21. <view class="choose" v-show="goodsInfo[specListName] && goodsInfo[specListName][0].name !== defaultSingleSkuName">
  22. <text v-if="!selectArr.every(val => val == '')">已选:{{ selectArr.join(' ') }}</text>
  23. </view>
  24. </view>
  25. </view>
  26. <view class="specification-content">
  27. <view v-show="goodsInfo[specListName][0].name !== defaultSingleSkuName" class="specification-item" v-for="(item, index1) in goodsInfo[specListName]" :key="index1">
  28. <view class="item-title">{{ item.name }}</view>
  29. <view class="item-wrapper">
  30. <view class="item-content" @tap="skuClick(item_value, index1, $event, index2)" v-for="(item_value, index2) in item.list"
  31. :key="index2" :class="[item_value.ishow ? '' : 'noactived', subIndex[index1] == index2 ? 'actived' : '']"
  32. :style="[item_value.ishow ? '' : disableStyle,
  33. item_value.ishow ? btnStyle :'',
  34. subIndex[index1] == index2 ? activedStyle : ''
  35. ]">
  36. {{ item_value.name }}
  37. </view>
  38. </view>
  39. </view>
  40. <view style="display: flex;">
  41. <view style="flex: 1;">
  42. <text style="font-size: 26rpx; color: #333; line-height: 50rpx;">数量</text>
  43. </view>
  44. <view style="flex: 4;text-align: right;">
  45. <number-box :min="minBuyNum" :max="maxBuyNum" :step="stepBuyNum" v-model="selectNum"
  46. :positive-integer="true">
  47. </number-box>
  48. </view>
  49. </view>
  50. </view>
  51. </scroll-view>
  52. <view class="close" @click="close('close')" v-if="showClose">
  53. <image class="close-item" :src="closeImage"></image>
  54. </view>
  55. </view>
  56. <view class="btn-option">
  57. <view class="btn-wrapper" v-if="outFoStock || mode == 4">
  58. <view class="sure" style="color:#ffffff;background-color:#cccccc">{{ noStockText }}</view>
  59. </view>
  60. <view class="btn-wrapper" v-else-if="mode == 1">
  61. <view class="sure add-cart" style="border-radius:38rpx 0rpx 0rpx 38rpx;" @click="addCart" :style="'color:'+addCartColor+';background:'+addCartBackgroundColor">{{ addCartText }}</view>
  62. <view class="sure" style="border-radius:0rpx 38rpx 38rpx 0rpx;" @click="buyNow" :style="'color:'+buyNowColor+';background-color:'+buyNowBackgroundColor">{{ buyNowText }}</view>
  63. </view>
  64. <view class="btn-wrapper" v-else-if="mode == 2">
  65. <view class="sure add-cart" @click="addCart" :style="'color:'+addCartColor+';background:'+addCartBackgroundColor">{{ addCartText }}</view>
  66. </view>
  67. <view class="btn-wrapper" v-else-if="mode == 3">
  68. <view class="sure" @click="buyNow" :style="'color:'+buyNowColor+';background:'+buyNowBackgroundColor">{{ buyNowText }}</view>
  69. </view>
  70. </view>
  71. <!-- 页面结束 -->
  72. </view>
  73. <!-- 页面内容结束 -->
  74. </view>
  75. </template>
  76. <script>
  77. import NumberBox from './number-box'
  78. var that; // 当前页面对象
  79. var vk; // 自定义函数集
  80. export default {
  81. name: 'GoodsSkuPopup',
  82. components: {
  83. NumberBox
  84. },
  85. props: {
  86. // true 组件显示 false 组件隐藏
  87. value: {
  88. Type: Boolean,
  89. default: false
  90. },
  91. // vk云函数路由模式参数开始-----------------------------------------------------------
  92. // 商品id
  93. goodsId: {
  94. Type: String,
  95. default: ""
  96. },
  97. // vk路由模式框架下的云函数地址
  98. action: {
  99. Type: String,
  100. default: ""
  101. },
  102. // vk云函数路由模式参数结束-----------------------------------------------------------
  103. // 该商品已抢完时的按钮文字
  104. noStockText: {
  105. Type: String,
  106. default: "该商品已抢完"
  107. },
  108. // 库存文字
  109. stockText: {
  110. Type: String,
  111. default: "库存"
  112. },
  113. // 商品表id的字段名
  114. goodsIdName: {
  115. Type: String,
  116. default: "_id"
  117. },
  118. // sku表id的字段名
  119. skuIdName: {
  120. Type: String,
  121. default: "_id"
  122. },
  123. // sku_list的字段名
  124. skuListName: {
  125. Type: String,
  126. default: "sku_list"
  127. },
  128. // spec_list的字段名
  129. specListName: {
  130. Type: String,
  131. default: "spec_list"
  132. },
  133. // stock的字段名
  134. stockName: {
  135. Type: String,
  136. default: "stock"
  137. },
  138. // sku_name的字段名
  139. skuName: {
  140. Type: String,
  141. default: "sku_name"
  142. },
  143. // sku组合路径的字段名
  144. skuArrName: {
  145. Type: String,
  146. default: "sku_name_arr"
  147. },
  148. // 默认单规格时的规格组名称
  149. defaultSingleSkuName: {
  150. Type: String,
  151. default: "默认"
  152. },
  153. // 模式 1:都显示 2:只显示购物车 3:只显示立即购买 4:显示缺货按钮 默认 1
  154. mode: {
  155. Type: Number,
  156. default: 1
  157. },
  158. // 点击遮罩是否关闭组件 true 关闭 false 不关闭 默认true
  159. maskCloseAble: {
  160. Type: Boolean,
  161. default: true
  162. },
  163. // 顶部圆角值
  164. borderRadius: {
  165. Type: [String, Number],
  166. default: 0
  167. },
  168. // 商品缩略图字段名(未选择sku时)
  169. goodsThumbName: {
  170. Type: [String],
  171. default: "goods_thumb"
  172. },
  173. // 最小购买数量
  174. minBuyNum: {
  175. Type: Number,
  176. default: 1
  177. },
  178. // 最大购买数量
  179. maxBuyNum: {
  180. Type: Number,
  181. default: 100000
  182. },
  183. // 每次点击后的数量
  184. stepBuyNum: {
  185. Type: Number,
  186. default: 1
  187. },
  188. // 自定义获取商品信息的函数
  189. customAction: {
  190. Type: [Function],
  191. default: null
  192. },
  193. // 价格的字体颜色
  194. priceColor: {
  195. Type: String,
  196. default: "#fe560a"
  197. },
  198. // 立即购买按钮的文字
  199. buyNowText: {
  200. Type: String,
  201. default: "立即购买"
  202. },
  203. // 立即购买按钮的字体颜色
  204. buyNowColor: {
  205. Type: String,
  206. default: "#ffffff"
  207. },
  208. // 立即购买按钮的背景颜色
  209. buyNowBackgroundColor: {
  210. Type: String,
  211. default: "linear-gradient(to right, $fuint-theme, $fuint-theme)"
  212. },
  213. // 加入购物车按钮的文字
  214. addCartText: {
  215. Type: String,
  216. default: "加入购物车"
  217. },
  218. // 加入购物车按钮的字体颜色
  219. addCartColor: {
  220. Type: String,
  221. default: "#ffffff"
  222. },
  223. // 加入购物车按钮的背景颜色
  224. addCartBackgroundColor: {
  225. Type: String,
  226. default: "linear-gradient(to right, $fuint-theme, $fuint-theme)"
  227. },
  228. // 不可点击时,按钮的样式
  229. disableStyle: {
  230. Type: Object,
  231. default: null
  232. },
  233. // 按钮点击时的样式
  234. activedStyle: {
  235. Type: Object,
  236. default: null
  237. },
  238. // 按钮常态的样式
  239. btnStyle: {
  240. Type: Object,
  241. default: null
  242. },
  243. // 是否显示右上角关闭按钮
  244. showClose: {
  245. Type: Boolean,
  246. default: true
  247. },
  248. // 关闭按钮的图片地址
  249. closeImage: {
  250. Type: String,
  251. default: "https://img.alicdn.com/imgextra/i1/121022687/O1CN01ImN0O11VigqwzpLiK_!!121022687.png"
  252. },
  253. // 默认库存数量 (未选择sku时)
  254. defaultStock: {
  255. Type: Number,
  256. default: 0
  257. },
  258. // 默认显示的价格 (未选择sku时)
  259. defaultPrice: {
  260. Type: Number,
  261. default: 0
  262. },
  263. },
  264. data() {
  265. return {
  266. complete: false, // 组件是否加载完成
  267. goodsInfo: {}, // 商品信息
  268. isShow: false, // true 显示 false 隐藏
  269. initKey: true, // 是否已初始化
  270. shopItemInfo: {}, // 存放要和选中的值进行匹配的数据
  271. selectArr: [], // 存放被选中的值
  272. subIndex: [], // 是否选中 因为不确定是多规格还是单规格,所以这里定义数组来判断
  273. selectShop: {}, // 存放最后选中的商品
  274. selectNum: this.minBuyNum, // 选中数量
  275. outFoStock: false, // 是否全部sku都缺货
  276. };
  277. },
  278. mounted() {
  279. that = this;
  280. vk = that.vk;
  281. if (that.value) {
  282. that.open();
  283. }
  284. },
  285. methods: {
  286. // 初始化
  287. init() {
  288. // 清空之前的数据
  289. that.selectArr = [];
  290. that.subIndex = [];
  291. that.selectShop = {};
  292. that.selectNum = that.minBuyNum;
  293. that.outFoStock = false;
  294. that.shopItemInfo = {};
  295. let specListName = that.specListName;
  296. that.goodsInfo[specListName].map(item => {
  297. that.selectArr.push('');
  298. that.subIndex.push(-1);
  299. });
  300. that.checkItem(); // 计算sku里面规格形成路径
  301. that.checkInpath(-1); // 传-1是为了不跳过循环
  302. that.autoClickSku(); // 自动选择sku策略
  303. },
  304. // 使用vk路由模式框架获取商品信息
  305. findGoodsInfo() {
  306. if (typeof vk == "undefined") {
  307. that.toast("custom-action必须是function");
  308. return false;
  309. }
  310. vk.callFunction({
  311. url: that.action,
  312. title: '请求中...',
  313. data: {
  314. goods_id: that.goodsId
  315. },
  316. success(data) {
  317. that.updateGoodsInfo(data.goodsInfo);
  318. }
  319. });
  320. },
  321. // 更新商品信息(库存、名称、图片)
  322. updateGoodsInfo(goodsInfo) {
  323. let skuListName = that.skuListName;
  324. if (JSON.stringify(that.goodsInfo) === "{}" || that.goodsInfo[that.goodsIdName] !== goodsInfo[that.goodsIdName]) {
  325. that.goodsInfo = goodsInfo;
  326. that.initKey = true;
  327. } else {
  328. that.goodsInfo[skuListName] = goodsInfo[skuListName];
  329. }
  330. if (that.initKey) {
  331. that.initKey = false;
  332. that.init();
  333. }
  334. // 更新选中sku的库存信息
  335. let select_sku_info = that.getListItem(that.goodsInfo[skuListName], that.skuIdName, that.selectShop[that.skuIdName]);
  336. Object.assign(that.selectShop, select_sku_info);
  337. that.complete = true;
  338. that.$emit("open", true);
  339. that.$emit("input", true);
  340. },
  341. async open() {
  342. let findGoodsInfoRun = true;
  343. let skuListName = that.skuListName;
  344. if (that.customAction && typeof(that.customAction) === 'function') {
  345. let goodsInfo = await that.customAction();
  346. if (goodsInfo && typeof goodsInfo == "object" && JSON.stringify(goodsInfo) != "{}") {
  347. findGoodsInfoRun = false;
  348. that.updateGoodsInfo(goodsInfo);
  349. } else {
  350. that.toast("无法获取到商品信息");
  351. that.$emit("input", false);
  352. return false;
  353. }
  354. } else {
  355. if (findGoodsInfoRun) {
  356. that.findGoodsInfo();
  357. }
  358. }
  359. },
  360. // 监听 - 弹出层收起
  361. close(s) {
  362. if (s == "close") {
  363. that.$emit("input", false);
  364. that.$emit("close", "close");
  365. } else if (s == "mask") {
  366. if (that.maskCloseAble) {
  367. that.$emit("input", false);
  368. that.$emit("close", "mask");
  369. }
  370. }
  371. },
  372. moveHandle() {
  373. //禁止父元素滑动
  374. },
  375. // sku按钮的点击事件
  376. skuClick(value, index1, event, index2) {
  377. if (value.ishow) {
  378. if (that.selectArr[index1] != value.name) {
  379. that.$set(that.selectArr, index1, value.name);
  380. that.$set(that.subIndex, index1, index2);
  381. } else {
  382. that.$set(that.selectArr, index1, '');
  383. that.$set(that.subIndex, index1, -1);
  384. }
  385. that.checkInpath(index1);
  386. // 如果全部选完
  387. that.checkSelectShop();
  388. }
  389. },
  390. // 检测是否已经选完sku
  391. checkSelectShop() {
  392. // 如果全部选完
  393. if (that.selectArr.every(item => item != '')) {
  394. that.selectShop = that.shopItemInfo[that.selectArr];
  395. that.selectNum = that.minBuyNum;
  396. } else {
  397. that.selectShop = {};
  398. }
  399. },
  400. // 检查路径
  401. checkInpath(clickIndex) {
  402. let specListName = that.specListName;
  403. //循环所有属性判断哪些属性可选
  404. //当前选中的兄弟节点和已选中属性不需要循环
  405. let specList = that.goodsInfo[specListName];
  406. for (let i = 0, len = specList.length; i < len; i++) {
  407. if (i == clickIndex) {
  408. continue;
  409. }
  410. let len2 = specList[i].list.length;
  411. for (let j = 0; j < len2; j++) {
  412. if (that.subIndex[i] != -1 && j == that.subIndex[i]) {
  413. continue;
  414. }
  415. let choosed_copy = [...that.selectArr];
  416. that.$set(choosed_copy, i, specList[i].list[j].name);
  417. let choosed_copy2 = choosed_copy.filter(item => item !== '' && typeof item !== 'undefined');
  418. if (that.shopItemInfo.hasOwnProperty(choosed_copy2)) {
  419. specList[i].list[j].ishow = true;
  420. } else {
  421. specList[i].list[j].ishow = false;
  422. }
  423. }
  424. }
  425. that.$set(that.goodsInfo, specListName, specList);
  426. },
  427. // 计算sku里面规格形成路径
  428. checkItem() {
  429. let skuListName = that.skuListName;
  430. // console.time('计算有多小种可选路径需要的时间是');
  431. // 去除库存小于等于0的商品sku
  432. let skuList = that.goodsInfo[skuListName];
  433. let stockNum = 0;
  434. for (let i = 0; i < skuList.length; i++) {
  435. if (skuList[i][that.stockName] <= 0) {
  436. skuList.splice(i, 1);
  437. i--;
  438. } else {
  439. stockNum += skuList[i][that.stockName];
  440. }
  441. }
  442. if (stockNum <= 0) {
  443. that.outFoStock = true;
  444. }
  445. // 计算有多小种可选路径
  446. let result = skuList.reduce(
  447. (arrs, items) => {
  448. return arrs.concat(
  449. items[that.skuArrName].reduce(
  450. (arr, item) => {
  451. return arr.concat(
  452. arr.map(item2 => {
  453. // 利用对象属性的唯一性实现二维数组去重
  454. if (!that.shopItemInfo.hasOwnProperty([...item2, item])) {
  455. that.shopItemInfo[[...item2, item]] = items;
  456. }
  457. return [...item2, item];
  458. })
  459. );
  460. },
  461. [
  462. []
  463. ]
  464. )
  465. );
  466. },
  467. [
  468. []
  469. ]
  470. );
  471. },
  472. // 检测sku选项是否已全部选完,且有库存
  473. checkSelectComplete(obj = {}) {
  474. let selectShop = that.selectShop;
  475. if (selectShop && selectShop[that.skuIdName] !== undefined) {
  476. // 判断库存
  477. if (that.selectNum <= selectShop[that.stockName]) {
  478. if (typeof obj.success == "function") obj.success(selectShop);
  479. } else {
  480. that.toast(that.stockText + "不足", "none")
  481. }
  482. } else {
  483. that.toast("请先选择对应规格", "none");
  484. }
  485. },
  486. // 加入购物车
  487. addCart() {
  488. that.checkSelectComplete({
  489. success: function(selectShop) {
  490. selectShop.buy_num = that.selectNum;
  491. that.$emit("add-cart", selectShop);
  492. }
  493. });
  494. },
  495. // 立即购买
  496. buyNow() {
  497. that.checkSelectComplete({
  498. success: function(selectShop) {
  499. selectShop.buy_num = that.selectNum;
  500. that.$emit("buy-now", selectShop);
  501. }
  502. });
  503. },
  504. // 弹窗
  505. toast(title, icon) {
  506. uni.showToast({
  507. title: title,
  508. icon: icon
  509. });
  510. },
  511. // 获取对象数组中的某一个item,根据指定的键值
  512. getListItem(list, key, value) {
  513. let item;
  514. for (let i in list) {
  515. if (typeof value == "object") {
  516. if (JSON.stringify(list[i][key]) === JSON.stringify(value)) {
  517. item = list[i];
  518. break;
  519. }
  520. } else {
  521. if (list[i][key] === value) {
  522. item = list[i];
  523. break;
  524. }
  525. }
  526. }
  527. return item;
  528. },
  529. // 自动选择sku前提是只有一组sku,默认自动选择最前面的有库存的sku
  530. autoClickSku() {
  531. let skuList = that.goodsInfo[that.skuListName];
  532. let specListArr = that.goodsInfo[that.specListName];
  533. if (specListArr.length == 1) {
  534. let specList = specListArr[0].list;
  535. for (let i = 0; i < specList.length; i++) {
  536. let sku = that.getListItem(skuList, that.skuArrName, [specList[i].name]);
  537. if (sku) {
  538. that.skuClick(specList[i], 0, {}, i);
  539. break;
  540. }
  541. }
  542. }
  543. }
  544. },
  545. // 过滤器
  546. filters: {
  547. // 金额显示过滤器
  548. priceFilter(n = 0) {
  549. if (typeof n == "string") {
  550. n = parseFloat(n)
  551. }
  552. return n ? n.toFixed(2) : n
  553. }
  554. },
  555. // 计算属性
  556. computed: {
  557. },
  558. watch: {
  559. value: function(val) {
  560. if (val) {
  561. that.open();
  562. }
  563. },
  564. }
  565. };
  566. </script>
  567. <style lang="scss" scoped>
  568. /* sku弹出层 */
  569. .goods-sku-popup {
  570. position: fixed;
  571. left: 0;
  572. top: 0;
  573. right: 0;
  574. bottom: 0;
  575. z-index: 999999999999;
  576. overflow: hidden;
  577. &.show {
  578. display: block;
  579. .mask {
  580. animation: showPopup 0.2s linear both;
  581. }
  582. .layer {
  583. animation: showLayer 0.2s linear both;
  584. }
  585. }
  586. &.hide {
  587. .mask {
  588. animation: hidePopup 0.2s linear both;
  589. }
  590. .layer {
  591. animation: hideLayer 0.2s linear both;
  592. }
  593. }
  594. &.none {
  595. display: none;
  596. }
  597. .mask {
  598. position: fixed;
  599. top: 0;
  600. width: 100%;
  601. height: 100%;
  602. z-index: 1;
  603. background-color: rgba(0, 0, 0, 0.65);
  604. }
  605. .layer {
  606. display: flex;
  607. width: 100%;
  608. max-height: 1200rpx;
  609. flex-direction: column;
  610. position: fixed;
  611. z-index: 999999;
  612. bottom: 0;
  613. border-radius: 10rpx 10rpx 0 0;
  614. background-color: #ffffff;
  615. margin-top: 10rpx;
  616. overflow-y: scroll;
  617. .btn-option {
  618. padding: 1rpx;
  619. display: block;
  620. clear: both;
  621. margin-bottom: 60rpx;
  622. }
  623. .specification-wrapper {
  624. width: 100%;
  625. margin-top: 20rpx;
  626. padding: 30rpx 25rpx;
  627. box-sizing: border-box;
  628. .specification-wrapper-content {
  629. width: 100%;
  630. min-height: 300rpx;
  631. &::-webkit-scrollbar {
  632. /*隐藏滚轮*/
  633. display: none;
  634. }
  635. .specification-header {
  636. width: 100%;
  637. display: flex;
  638. flex-direction: row;
  639. position: relative;
  640. margin-bottom: 40rpx;
  641. .specification-left {
  642. width: 180rpx;
  643. height: 180rpx;
  644. flex: 0 0 180rpx;
  645. .product-img {
  646. width: 180rpx;
  647. height: 180rpx;
  648. background-color: #999999;
  649. }
  650. }
  651. .specification-right {
  652. flex: 1;
  653. padding: 0 35rpx 10rpx 28rpx;
  654. box-sizing: border-box;
  655. display: flex;
  656. flex-direction: column;
  657. justify-content: flex-end;
  658. font-weight: 500;
  659. .price-content {
  660. color: #fe560a;
  661. margin-bottom: 10rpx;
  662. .sign {
  663. font-size: 28rpx;
  664. margin-right: 4rpx;
  665. }
  666. .price {
  667. font-size: 44rpx;
  668. }
  669. }
  670. .inventory {
  671. font-size: 24rpx;
  672. color: #525252;
  673. margin-bottom: 14rpx;
  674. }
  675. .choose {
  676. font-size: 24rpx;
  677. color: #525252;
  678. min-height: 32rpx;
  679. }
  680. }
  681. }
  682. .specification-content {
  683. font-weight: 500;
  684. .specification-item {
  685. margin-bottom: 40rpx;
  686. &:last-child {
  687. margin-bottom: 0;
  688. }
  689. .item-title {
  690. margin-bottom: 15rpx;
  691. font-size: 32rpx;
  692. font-weight: bold;
  693. color: #000000;
  694. }
  695. .item-wrapper {
  696. display: flex;
  697. flex-direction: row;
  698. flex-flow: wrap;
  699. margin-bottom: -20rpx;
  700. .item-content {
  701. display: block;
  702. padding: 10rpx 20rpx;
  703. min-width: 110rpx;
  704. text-align: center;
  705. font-size: 24rpx;
  706. border-radius: 30rpx;
  707. background-color: #ffffff;
  708. color: #333333;
  709. margin-right: 20rpx;
  710. margin-bottom: 20rpx;
  711. border: 2rpx solid #cccccc;
  712. box-sizing: border-box;
  713. &.actived {
  714. border-color: #fe560a;
  715. color: #fe560a;
  716. }
  717. &.noactived {
  718. // background-color: #e4e4e4;
  719. // border-color: #e4e4e4;
  720. // color: #9e9e9e;
  721. // text-decoration: line-through;
  722. color: #c8c9cc;
  723. background: #f2f3f5;
  724. border-color: #f2f3f5;
  725. }
  726. }
  727. }
  728. }
  729. }
  730. }
  731. .close {
  732. position: absolute;
  733. top: 30rpx;
  734. right: 25rpx;
  735. width: 50rpx;
  736. height: 50rpx;
  737. text-align: center;
  738. line-height: 50rpx;
  739. .close-item {
  740. width: 40rpx;
  741. height: 40rpx;
  742. }
  743. }
  744. }
  745. .btn-wrapper {
  746. display: flex;
  747. width: 100%;
  748. height: 120rpx;
  749. flex: 0 0 120rpx;
  750. align-items: center;
  751. justify-content: space-between;
  752. padding: 0 26rpx;
  753. box-sizing: border-box;
  754. .layer-btn {
  755. width: 335rpx;
  756. height: 80rpx;
  757. border-radius: 40rpx;
  758. color: #fff;
  759. line-height: 80rpx;
  760. text-align: center;
  761. font-weight: 500;
  762. font-size: 28rpx;
  763. &.add-cart {
  764. background: #ffbe46;
  765. }
  766. &.buy {
  767. background: #fe560a;
  768. }
  769. }
  770. .sure {
  771. width: 698rpx;
  772. height: 80rpx;
  773. border-radius: 38rpx;
  774. color: #fff;
  775. line-height: 80rpx;
  776. text-align: center;
  777. font-weight: 500;
  778. font-size: 28rpx;
  779. background: #fe560a;
  780. }
  781. .sure.add-cart {
  782. background: #ff9402;
  783. }
  784. }
  785. }
  786. @keyframes showPopup {
  787. 0% {
  788. opacity: 0;
  789. }
  790. 100% {
  791. opacity: 1;
  792. }
  793. }
  794. @keyframes hidePopup {
  795. 0% {
  796. opacity: 1;
  797. }
  798. 100% {
  799. opacity: 0;
  800. }
  801. }
  802. @keyframes showLayer {
  803. 0% {
  804. transform: translateY(120%);
  805. }
  806. 100% {
  807. transform: translateY(0%);
  808. }
  809. }
  810. @keyframes hideLayer {
  811. 0% {
  812. transform: translateY(0);
  813. }
  814. 100% {
  815. transform: translateY(120%);
  816. }
  817. }
  818. }
  819. </style>