index.vue 25 KB

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