menu.vue 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600
  1. <template>
  2. <view class="u-wrap" :style="$isEmpty(goods)?'background-color: #FFFFFF;':''">
  3. <block v-show="!isLoading">
  4. <view class="goods_empty" v-if="$isEmpty(goods)">
  5. <image src="/pagesD/static/icon/goods_empty.png" mode=""></image>
  6. <text style="font-weight: 400;">该商铺还未上架商品</text>
  7. </view>
  8. <block v-else>
  9. <view class="u-search-box">
  10. <view class="nav">
  11. <view class="header">
  12. <view class="left overflow-hidden">
  13. <view class="flex align-center overflow-hidden" v-if="takeType == takeTypeGlobal.delivery">
  14. <image src="/pagesD/static/icon/location1.png" style="width: 30rpx; height: 30rpx;"
  15. class="margin-right-10">
  16. </image>
  17. <text v-if="$isEmpty(vuex_userId)" @tap="showLogin">去登录</text>
  18. <view class="" v-else @click="$jump('/pagesD/pages/address/address')">
  19. <text v-if="$isEmpty(address)">请完善地址信息</text>
  20. <view style="margin-right: 20rpx;" v-else>
  21. <text class="text-cut-1">{{address.address}}</text>
  22. </view>
  23. </view>
  24. </view>
  25. <view class="flex align-center overflow-hidden" v-else>
  26. <image src="/pagesD/static/icon/shop2.png" style="width: 30rpx; height: 30rpx;"
  27. class="margin-right-10">
  28. </image>
  29. <text style="font-size: 36rpx;font-weight: 800;">{{appProperties.shopName}}</text>
  30. </view>
  31. </view>
  32. <view @click="modalCheckedShow" class="cu-btn round sm" style="font-size: 24rpx;">
  33. <text class="center">{{takeType.substring(2,4)}}</text>
  34. <text class="cuIcon-order center text-bold"
  35. style="transform: rotate(90deg);margin-left: 6rpx;"></text>
  36. </view>
  37. </view>
  38. <view class="bg-white" v-if="$isNotEmpty(appProperties.shopNotice)">
  39. <u-notice-bar mode="horizontal" :list="[appProperties.shopNotice]"></u-notice-bar>
  40. </view>
  41. </view>
  42. </view>
  43. <view class="u-menu-wrap">
  44. <scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="scrollTop"
  45. :scroll-into-view="itemId">
  46. <view v-for="(item,index) in goods" :key="index" class="u-tab-item"
  47. :class="[current == index ? 'u-tab-item-active' : '']" @tap.stop="swichMenu(index)">
  48. <text class="u-line-1">{{item.goodsCategoryName}}</text>
  49. </view>
  50. </scroll-view>
  51. <scroll-view :scroll-top="scrollRightTop" scroll-y scroll-with-animation class="right-box"
  52. @scroll="rightScroll">
  53. <view class="page-view">
  54. <view class="class-item" :id="'item' + index" v-for="(item , index) in goods" :key="index">
  55. <view class="item-title">
  56. <text>{{item.goodsCategoryName}}</text>
  57. </view>
  58. <view class="item-container">
  59. <view class="thumb-box" v-for="(goods, index1) in item.goodsList" :key="index1">
  60. <image @click="$util.preview(goods.image)" class="item-menu-image" :src="goods.image" mode=""></image>
  61. <view class="item-content">
  62. <view class="name">{{goods.name}}</view>
  63. <view class="tips">
  64. {{ goods.description && goods.description != '' ? goods.description : goods.name }}
  65. </view>
  66. <view class="price_and_action">
  67. <text class="price">¥{{ goods.defaultPrice / 100 }}</text>
  68. <!-- 商品没有下架 -->
  69. <view v-if="goods.isSell">
  70. <!-- 要选商品规格详细信息 -->
  71. <view class="btn-group"
  72. v-if="goods.goodsPropertyVos && goods.goodsPropertyVos.length">
  73. <button type="primary" class="btn property_btn" hover-class="none"
  74. size="mini" @tap="showGoodDetailModal(goods)">
  75. 选规格
  76. </button>
  77. <view class="dot" v-show="goodsCartNum(goods.id)">
  78. {{ goodsCartNum(goods.id) }}
  79. </view>
  80. </view>
  81. <!-- 可以直接添加,不用选择商品属性 -->
  82. <view class="btn-group" v-else>
  83. <button type="default" v-show="goodsCartNum(goods.id)" plain
  84. class="btn reduce_btn" size="mini" hover-class="none"
  85. @tap="handleReduceFromCart(goods)">
  86. <view class="iconfont iconsami-select"></view>
  87. </button>
  88. <view class="number" v-show="goodsCartNum(goods.id)">
  89. {{ goodsCartNum(goods.id) }}
  90. </view>
  91. <button type="primary" class="btn add_btn" size="min" hover-class="none"
  92. @tap="handleAddToCart(goods)">
  93. <view class="iconfont iconadd-select"></view>
  94. </button>
  95. </view>
  96. </view>
  97. <!-- 已经下架了 -->
  98. <view v-else>
  99. <view class="btn-group">
  100. <button type="primary" disabled class="btn property_btn"
  101. style="text-decoration: line-through;background-color: rgba(255, 148, 71,.6);"
  102. size="mini">
  103. 已售空
  104. </button>
  105. </view>
  106. </view>
  107. </view>
  108. </view>
  109. </view>
  110. </view>
  111. </view>
  112. </view>
  113. </scroll-view>
  114. </view>
  115. </block>
  116. </block>
  117. <view class="" v-show="!isLoading && $isNotEmpty(goods)">
  118. <!-- 购物车栏 begin -->
  119. <view class="cart-box" v-if="cart && cart.length">
  120. <view class="mark" @tap="cartPopupVisible = !cartPopupVisible">
  121. <image src="/pagesD/static/icon/cart.png" class="cart-img"></image>
  122. <view class="tag">{{ getCartGoodsNumber }}</view>
  123. </view>
  124. <view style="color: #202224;font-size: 34rpx;font-weight: 800;" class="flex-sub" @tap="cartPopupVisible = !cartPopupVisible">¥{{ getCartGoodsPrice / 100 }}</view>
  125. <view v-if="!appProperties.shopStatus"><button type="primary" class="pay-btn" disabled>商家休息中</button>
  126. </view>
  127. <view v-else-if="isBusinessTime">
  128. <button :style="!disabledPay?'background-color: #FF9447;':''" type="primary" class="pay-btn"
  129. @tap="toPay()"
  130. :disabled="disabledPay">{{ disabledPay ? `差${spread / 100}元起送` : '去结算' }}</button>
  131. </view>
  132. <view v-else><button type="primary" class="pay-btn" disabled>未在营业时段</button></view>
  133. </view>
  134. <!-- 购物车栏 end -->
  135. <!-- 商品详情模态框 begin -->
  136. <modal :show="goodDetailModalVisible" class="good-detail-modal" color="#5A5B5C" width="90%" custom
  137. padding="0rpx" radius="12rpx">
  138. <view class="cover">
  139. <image v-if="currentGoods.image" :src="currentGoods.image" class="image"></image>
  140. <view class="btn-group">
  141. <image src="/pagesD/static/icon/close.png" @tap="closeGoodDetailModal"></image>
  142. </view>
  143. </view>
  144. <scroll-view class="detail" scroll-y style="padding-bottom: 100px;">
  145. <view class="wrapper">
  146. <view class="basic">
  147. <view class="name">{{ currentGoods.name }}</view>
  148. <view class="tips">{{ currentGoods.description }}</view>
  149. </view>
  150. <!-- 商品的额外属性 -->
  151. <view class="properties"
  152. v-if="currentGoods.goodsPropertyVos && currentGoods.goodsPropertyVos.length">
  153. <view class="property" v-for="(goodsPropertyVo, index) in currentGoods.goodsPropertyVos"
  154. :key="index">
  155. <view class="title">
  156. <text class="name">
  157. <text v-if="goodsPropertyVo.required" style="color: red">*</text>
  158. {{ goodsPropertyVo.category }}
  159. </text>
  160. </view>
  161. <view class="values">
  162. <view class="value" v-for="(property, index2) in goodsPropertyVo.propertyList"
  163. :key="index2" :class="{ default: property.isDefault }"
  164. @tap="changePropertyDefault(index, index2)">
  165. {{ property.propertyOption }}
  166. {{ property.rebasePrice && property.rebasePrice >=0 ? '¥' + property.rebasePrice / 100 : property.extraPrice / 100 ? '¥' + property.extraPrice / 100 : '' }}
  167. </view>
  168. </view>
  169. </view>
  170. </view>
  171. </view>
  172. </scroll-view>
  173. <view class="action">
  174. <!-- 价格和属性 -->
  175. <view class="left">
  176. <view class="price">¥{{ currentGoods.realPrice / 100 }}</view>
  177. <view class="props" v-if="getGoodSelectedProps(currentGoods)">
  178. {{ getGoodSelectedProps(currentGoods) }}
  179. </view>
  180. </view>
  181. <!-- 模态窗里面增减商品 -->
  182. <view class="btn-group">
  183. <button type="default" plain class="btn" size="mini" hover-class="none"
  184. @tap="changeCurrentGoodsNumber(-1)">
  185. <view class="iconfont iconsami-select"></view>
  186. </button>
  187. <view class="number">{{ currentGoods.number }}</view>
  188. <button type="primary" class="btn" size="min" hover-class="none"
  189. @tap="changeCurrentGoodsNumber(1)">
  190. <view class="iconfont iconadd-select"></view>
  191. </button>
  192. </view>
  193. </view>
  194. <view class="add-to-cart-btn" @tap="addCurrentGoodsToCart()">
  195. <view>确认</view>
  196. </view>
  197. </modal>
  198. <!-- 商品详情模态框 end -->
  199. <!-- 购物车详情popup -->
  200. <u-popup z-index="99" v-model="cartPopupVisible" mode="bottom">
  201. <view class="cart-popup">
  202. <view class="top"><text @tap="handleCartClear">清空</text></view>
  203. <scroll-view class="cart-list" scroll-y>
  204. <view class="wrapper">
  205. <view class="item" v-for="(goods, index) in cart" :key="index">
  206. <view class="left">
  207. <view class="name">{{ goods.name }}</view>
  208. <view class="props">{{ getGoodSelectedProps(goods) }}</view>
  209. </view>
  210. <view class="center">
  211. <text>¥{{ goods.realPrice / 100 }}</text>
  212. </view>
  213. <view class="right">
  214. <!-- 购物车里添加数量 -->
  215. <button type="default" plain size="mini" class="btn" hover-class="none"
  216. @tap="handleCartItemReduce(index)">
  217. <view class="iconfont iconsami-select"></view>
  218. </button>
  219. <view class="number">{{ goods.number }}</view>
  220. <!-- 购物车里减少数量 -->
  221. <button type="primary" class="btn" size="min" hover-class="none"
  222. @tap="handleCartItemAdd(index)">
  223. <view class="iconfont iconadd-select"></view>
  224. </button>
  225. </view>
  226. </view>
  227. <view class="item" v-if="takeType == takeTypeGlobal.package">
  228. <view class="left">
  229. <view class="name">包装费</view>
  230. </view>
  231. <view class="center">
  232. <text>¥{{ appProperties.packingPrice / 100 }}</text>
  233. </view>
  234. </view>
  235. <view class="item" v-if="takeType == takeTypeGlobal.delivery">
  236. <view class="left">
  237. <view class="name">包装费</view>
  238. </view>
  239. <view class="center">
  240. <text>¥{{ appProperties.packingPrice / 100 }}</text>
  241. </view>
  242. <view class="left">
  243. <view class="name">配送费</view>
  244. </view>
  245. <view class="center">
  246. <text>¥{{ appProperties.sendingPrice / 100 }}</text>
  247. </view>
  248. </view>
  249. </view>
  250. </scroll-view>
  251. </view>
  252. </u-popup>
  253. <!-- 购物车详情popup -->
  254. </view>
  255. <toast ref="toast"></toast>
  256. <login ref="login" @signIn="signIn"></login>
  257. <loading ref="loading" type="3" theme="2" />
  258. <modal-checked @confirm="modalConfirm" ref="modalChecked" :list="checkList"></modal-checked>
  259. </view>
  260. </template>
  261. <script>
  262. import login from "../../comps/login.vue"
  263. import modal from '../../comps/modal/modal.vue';
  264. import modalChecked from "../../comps/modal-checked.vue"
  265. import popupLayer from '../../comps/popup-layer/popup-layer.vue';
  266. import global from "@/assets/http/global.js"
  267. let _this = this
  268. export default {
  269. components: {
  270. modal,
  271. popupLayer,
  272. login,
  273. modalChecked
  274. },
  275. data() {
  276. return {
  277. isLoading: true,
  278. shopId: '',
  279. //是否有必点商品
  280. hasMustOrder:false,
  281. address: {},
  282. cart: [],
  283. takeType: "店内堂食",
  284. takeTypeGlobal: global.takeType,
  285. shopDetail: {},
  286. appProperties: {
  287. "shopName": "",
  288. "sendingPrice": 0, // 商家配送费
  289. "packingPrice": 0, // 包装费
  290. "sendingNeedLeastPrice": 0, // 起送需要的最低价格
  291. "businessStartTime": '', // 营业开始时间, 开始的点必须在结束之前
  292. "businessEndTime": '', // 营业结束时间
  293. "shopStatus": true, // 商家营业状态
  294. "shopNotice": '', // 公告
  295. },
  296. goods: {},
  297. currentCategoryName: '', // 默认分类
  298. goodDetailModalVisible: false, //是否饮品详情模态框
  299. currentGoods: {}, // 当前商品
  300. selectedPropertyList: [], // 当前商品的属性
  301. cartPopupVisible: false,
  302. //座位号
  303. seatNum: 0,
  304. /**
  305. * uview菜单begin
  306. */
  307. scrollTop: 0, //tab标题的滚动条位置
  308. oldScrollTop: 0,
  309. current: 0, // 预设当前项的值
  310. menuHeight: 0, // 左边菜单的高度
  311. menuItemHeight: 0, // 左边菜单item的高度
  312. itemId: '', // 栏目右边scroll-view用于滚动的id
  313. goods: [],
  314. menuItemPos: [],
  315. arr: [],
  316. scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
  317. timer: null, // 定时器
  318. /**
  319. * u-view菜单end
  320. */
  321. checkList: [{
  322. //店内堂食
  323. name: "扫一扫店内下单",
  324. icon: "/pagesD/static/icon/order.png",
  325. },
  326. {
  327. name: "到店自取",
  328. icon: "/pagesD/static/icon/pickup.png",
  329. }
  330. ]
  331. }
  332. },
  333. onLoad(options) {
  334. this.$refs.loading.showLoading()
  335. _this = this
  336. if (this.$isNotEmpty(options.q)) {
  337. //获取普通二维码的参数
  338. let params = this.$util.getUrlParams(options.q)
  339. this.shopId = params.shopId
  340. this.seatNum = params.seatNum
  341. }
  342. if (this.$isNotEmpty(options.shopId)) {
  343. this.shopId = options.shopId
  344. }
  345. if (this.$isNotEmpty(options.seatNum)) {
  346. this.seatNum = options.seatNum
  347. }
  348. this.init()
  349. },
  350. onShow() {
  351. this.address = this.$util.getBackParams('addressData')
  352. if (this.$isEmpty(this.address)) {
  353. this.fetchAddress()
  354. }
  355. },
  356. onReady() {
  357. this.getMenuItemTop()
  358. },
  359. computed: {
  360. isBusinessTime: {
  361. get() {
  362. return this.timeRange(this.appProperties.businessStartTime, this.appProperties.businessEndTime)
  363. }
  364. },
  365. // 计算单个饮品添加到购物车的数量
  366. goodsCartNum() {
  367. return id =>
  368. this.cart.reduce((acc, cur) => {
  369. if (cur.id === id) {
  370. return (acc += cur.number);
  371. }
  372. return acc;
  373. }, 0);
  374. },
  375. // 计算该类商品的购买总数
  376. menuCartNum() {
  377. return goodsCategoryName =>
  378. this.cart.reduce((acc, cur) => {
  379. if (cur.goodsCategoryName === goodsCategoryName) {
  380. return (acc += cur.number);
  381. }
  382. return acc;
  383. }, 0);
  384. },
  385. // 计算购物车总数
  386. getCartGoodsNumber() {
  387. return this.cart.reduce((acc, cur) => acc + cur.number, 0);
  388. },
  389. // 计算购物车总价,
  390. getCartGoodsPrice() {
  391. let totalPrice = 0;
  392. this.cart.forEach(goods => {
  393. totalPrice += goods.realPrice * goods.number;
  394. });
  395. //如果是到店自取,要计算包装费
  396. if (this.takeType == this.takeTypeGlobal.package) {
  397. totalPrice += this.appProperties.packingPrice
  398. }
  399. //如果是商家配送,要计算配送费和包装费
  400. if (this.takeType == this.takeTypeGlobal.delivery) {
  401. totalPrice += this.appProperties.packingPrice + this.appProperties.sendingPrice;
  402. }
  403. return totalPrice;
  404. }, // 是否达到起送价
  405. disabledPay() {
  406. return this.takeType == this.takeTypeGlobal.delivery && this.getCartGoodsPrice < this.appProperties
  407. .sendingNeedLeastPrice ? true : false;
  408. }, // 差多少元起送
  409. spread() {
  410. if (this.takeType != this.takeTypeGlobal.delivery) return;
  411. return this.appProperties.sendingNeedLeastPrice - this.getCartGoodsPrice;
  412. }
  413. },
  414. watch: {
  415. cart: {
  416. handler: (newV, oldV) => {
  417. _this.putCart()
  418. },
  419. deep: true,
  420. }
  421. },
  422. methods: {
  423. modalCheckedShow() {
  424. this.$refs.modalChecked.showModal()
  425. },
  426. timeRange(beginTime, endTime) { //判断是否在查询时间
  427. var strb = beginTime.split(":");
  428. if (strb.length != 3) {
  429. return false;
  430. }
  431. var stre = endTime.split(":");
  432. if (stre.length != 3) {
  433. return false;
  434. }
  435. var b = new Date();
  436. var e = new Date();
  437. var n = new Date();
  438. if (parseInt(strb[0]) >= parseInt(stre[0])) {
  439. b.setDate(b.getDate() - 1)
  440. }
  441. console.log(b, ",,,");
  442. console.log(e, ",,,");
  443. console.log(n, ",,,");
  444. b.setHours(strb[0]);
  445. b.setMinutes(strb[1]);
  446. b.setSeconds(strb[2]);
  447. e.setHours(stre[0]);
  448. e.setMinutes(stre[1]);
  449. e.setSeconds(stre[2]);
  450. if (n.getTime() - b.getTime() > 0 && n.getTime() - e.getTime() < 0) {
  451. return true;
  452. } else {
  453. return false;
  454. }
  455. },
  456. //页面初始化
  457. async init() {
  458. await this.initShop()
  459. await this.initMenu()
  460. await this.initCart()
  461. this.initShopConfig()
  462. },
  463. async initShop() {
  464. let params = {
  465. id: this.shopId
  466. }
  467. let res=await this.$api.shop.detail(params)
  468. this.shopDetail = res.data
  469. this.appProperties.shopName=this.shopDetail.name
  470. },
  471. initShopConfig(){
  472. let params={
  473. shopId:this.shopId
  474. }
  475. this.$api.shopConfig.list(params).then(res=>{
  476. if (this.$isNotEmpty(res.data.records)) {
  477. this.$util.objectCopy(this.appProperties,res.data.records[0])
  478. this.appProperties.shopName=this.shopDetail.name
  479. }
  480. })
  481. },
  482. async initMenu() {
  483. let res = await this.$api.goods.menu({
  484. shopId: this.shopId
  485. })
  486. this.goods = res.data;
  487. this.hasMustOrder= this.goods.some(item=>item.goodsCategoryName.includes(this.$global.mustOrder))
  488. setTimeout(() => {
  489. _this.$refs.loading.hide()
  490. _this.isLoading = false
  491. if (_this.$isNotEmpty(_this.goods) && _this.$isEmpty(_this.seatNum)) {
  492. _this.$nextTick(function() {
  493. _this.modalCheckedShow()
  494. })
  495. }
  496. }, 400)
  497. },
  498. //初始化购物车
  499. initCart() {
  500. this.cartKey = this.$global.cart_prefix + this.shopId
  501. if (this.$isEmpty(this.$cache.get(this.cartKey))) {
  502. return
  503. }
  504. this.cart = this.$cache.get(this.cartKey)
  505. },
  506. putCart() {
  507. this.$cache.put(this.cartKey, this.cart)
  508. },
  509. modalConfirm(index) {
  510. let takeType = this.checkList[index].name
  511. if (takeType == '扫一扫店内下单') {
  512. this.scanCodeOrder()
  513. this.takeType = this.$global.takeType.dine_in
  514. return
  515. }
  516. this.takeType = this.checkList[index].name
  517. if (this.takeType == this.takeTypeGlobal.delivery) {
  518. this.takeout()
  519. }
  520. },
  521. scanCodeOrder() {
  522. uni.scanCode({
  523. success: (res) => {
  524. _this.seatNum = this.$util.getUrlParams(res.result).seatNum
  525. if (_this.$isEmpty(_this.seatNum)) {
  526. _this.$dialog.showModal('非点餐二维码!', false)
  527. _this.modalCheckedShow()
  528. return
  529. }
  530. },
  531. fail: (err) => {
  532. _this.modalCheckedShow()
  533. }
  534. })
  535. },
  536. // 选择商家配送的方式下单
  537. takeout() {
  538. if (this.$isEmpty(this.vuex_userId)) {
  539. this.showLogin()
  540. return
  541. }
  542. if (this.$isEmpty(this.address) && this.$isNotEmpty(this.vuex_userId)) {
  543. uni.navigateTo({
  544. url: "../address/address"
  545. })
  546. }
  547. },
  548. // 显示当前商品的详情信息
  549. showGoodDetailModal(currentGoods) {
  550. currentGoods.number = 1;
  551. currentGoods.realPrice = this.getGoodsRealPrice(currentGoods);
  552. currentGoods.propertyStr = this.getGoodSelectedProps(currentGoods);
  553. this.currentGoods = JSON.parse(JSON.stringify(currentGoods)); // 深拷贝
  554. this.goodDetailModalVisible = true;
  555. this.selectedPropertyList = [];
  556. console.log('显示当前商品的详情信息', this.currentGoods);
  557. },
  558. // 获取商品具体价格,如大杯商品,小杯商品价格不同
  559. getGoodsRealPrice(goods) {
  560. console.log(goods);
  561. let realPrice = goods.realPrice;
  562. if (goods.goodsPropertyVos && goods.goodsPropertyVos.length) {
  563. let vos = goods.goodsPropertyVos;
  564. for (let i = 0; i < vos.length; i++) {
  565. let propertyList = vos[i].propertyList;
  566. for (let j = 0; j < propertyList.length; j++) {
  567. if (propertyList[j].isDefault && propertyList[j].realPrice) {
  568. realPrice = propertyList[j].realPrice;
  569. break;
  570. }
  571. }
  572. }
  573. }
  574. return realPrice;
  575. },
  576. // 显示当前商品的详情信息
  577. showGoodDetailModal(currentGoods) {
  578. currentGoods.number = 1;
  579. currentGoods.realPrice = this.getGoodsRealPrice(currentGoods);
  580. currentGoods.propertyStr = this.getGoodSelectedProps(currentGoods);
  581. this.currentGoods = JSON.parse(JSON.stringify(currentGoods)); // 深拷贝
  582. this.goodDetailModalVisible = true;
  583. this.selectedPropertyList = [];
  584. console.log('显示当前商品的详情信息', this.currentGoods);
  585. },
  586. closeGoodDetailModal() {
  587. // 关闭饮品详情模态框
  588. this.goodDetailModalVisible = false;
  589. },
  590. // 改变商品的某个默认属性值 要区分必选和多选的属性
  591. changePropertyDefault(categoryIndex, propertyIndex) {
  592. let propertyCategory = this.currentGoods.goodsPropertyVos[categoryIndex].category;
  593. let propertyList = this.currentGoods.goodsPropertyVos[categoryIndex].propertyList;
  594. if (propertyCategory != '加料') {
  595. // 必须选一个
  596. propertyList.forEach(property => (property.isDefault = false)); // 重置所有默认选择状态重新选择
  597. propertyList[propertyIndex].isDefault = true;
  598. if (propertyCategory == '大小') {
  599. this.currentGoods.realPrice = this.currentGoods.realPrice - this.currentGoods.defaultPrice +
  600. propertyList[propertyIndex].rebasePrice;
  601. this.currentGoods.defaultPrice = propertyList[propertyIndex].rebasePrice;
  602. this.currentGoods.number = 1;
  603. this.currentGoods.propertyStr = this.getGoodSelectedProps(this.currentGoods);
  604. }
  605. } else {
  606. // 可选项 加料或减料
  607. if (propertyList[propertyIndex].isDefault) {
  608. this.currentGoods.realPrice -= propertyList[propertyIndex].extraPrice;
  609. propertyList[propertyIndex].isDefault = false;
  610. } else {
  611. this.currentGoods.realPrice += propertyList[propertyIndex].extraPrice;
  612. propertyList[propertyIndex].isDefault = true;
  613. }
  614. }
  615. },
  616. // 计算当前饮品所选属性,空格隔开
  617. getGoodSelectedProps(goods) {
  618. if (goods.goodsPropertyVos) {
  619. let propertyStr = '';
  620. goods.goodsPropertyVos.forEach(goodsPropertyVo => {
  621. goodsPropertyVo.propertyList.forEach(property => {
  622. if (property.isDefault) {
  623. propertyStr += property.propertyOption + ' ';
  624. }
  625. });
  626. });
  627. return propertyStr;
  628. }
  629. return '';
  630. },
  631. // 改变当前商品数量
  632. changeCurrentGoodsNumber(number) {
  633. this.currentGoods.number += number;
  634. if (this.currentGoods.number < 0) {
  635. this.currentGoods.number = 0;
  636. } else if (this.currentGoods.number >= 100) {
  637. this.currentGoods.number = 100;
  638. }
  639. },
  640. // 将当前商品加入到购物车
  641. addCurrentGoodsToCart() {
  642. if (this.currentGoods && this.currentGoods.number) {
  643. this.handleAddToCart(this.currentGoods);
  644. }
  645. this.closeGoodDetailModal();
  646. },
  647. // 减少购物车里的商品数量, goodsId和propertyStr去比较
  648. handleReduceFromCart(goods) {
  649. let propertyStr = this.getGoodSelectedProps(goods);
  650. let cart = this.cart;
  651. for (let i = cart.length - 1; i >= 0; i--) {
  652. if (cart[i].id == goods.id && cart[i].propertyStr == propertyStr) {
  653. if (cart[i].number <= 1) cart.splice(i, 1);
  654. else cart[i].number -= 1;
  655. return;
  656. }
  657. }
  658. },
  659. // 添加到购物车
  660. handleAddToCart(goods) {
  661. let propertyStr = this.getGoodSelectedProps(goods);
  662. goods.propertyStr = propertyStr;
  663. let cart = this.cart;
  664. for (let i = cart.length - 1; i >= 0; i--) {
  665. if (cart[i].id == goods.id && cart[i].propertyStr == propertyStr) {
  666. cart[i].number++;
  667. return;
  668. }
  669. }
  670. // 购物车里不存在
  671. if (!goods.number) goods.number = 1;
  672. this.cart.push(JSON.parse(JSON.stringify(goods)));
  673. },
  674. // 清空购物车
  675. handleCartClear() {
  676. let that = this;
  677. uni.showModal({
  678. title: '提示',
  679. content: '确定清空购物车么',
  680. success: ({
  681. confirm
  682. }) => {
  683. if (confirm) {
  684. that.cartPopupVisible = false;
  685. this.cart = []
  686. }
  687. }
  688. });
  689. },
  690. // 在购物车里操作商品数量
  691. handleCartItemAdd(index) {
  692. this.cart[index].number += 1;
  693. },
  694. handleCartItemReduce(index) {
  695. if (this.cart[index].number === 1) {
  696. this.cart.splice(index, 1);
  697. } else {
  698. this.cart[index].number -= 1;
  699. }
  700. if (!this.cart.length) {
  701. this.cartPopupVisible = false;
  702. }
  703. },
  704. toPay() {
  705. this.cartPopupVisible = false;
  706. if (this.$isEmpty(this.vuex_userId)) {
  707. this.showLogin()
  708. return
  709. }
  710. let orderInfo = {
  711. shopDetail: this.shopDetail,
  712. shopId: this.shopId,
  713. cart: this.cart,
  714. cartKey: this.cartKey,
  715. takeType: this.takeType,
  716. address: this.address,
  717. store: this.appProperties,
  718. address: this.address,
  719. seatNum: this.seatNum
  720. }
  721. this.$u.vuex('vuex_orderInfo', orderInfo)
  722. uni.navigateTo({
  723. url: '../pay/pay'
  724. })
  725. },
  726. fetchAddress() {
  727. if (this.$isEmpty(this.vuex_userId)) {
  728. return
  729. }
  730. let params = {
  731. userId: this.vuex_userId,
  732. isDefault: true
  733. }
  734. this.$api.address.list(params).then(res => {
  735. this.address = res.data.records[0]
  736. })
  737. },
  738. // 登录 begin
  739. showLogin() {
  740. this.$refs.login.show()
  741. },
  742. showPhoneModal() {
  743. this.$refs.login.showPhoneModal()
  744. },
  745. signIn(resp) {
  746. //获取手机号
  747. if (this.$isEmpty(this.vuex_phone)) {
  748. this.showPhoneModal()
  749. }
  750. this.$refs.toast.info('登录成功')
  751. this.fetchAddress()
  752. },
  753. async login(e) {
  754. if (this.$isEmpty(this.vuex_userId)) {
  755. this.showLogin()
  756. return
  757. } else {
  758. this.userInfo = this.$cache.get('userInfo')
  759. }
  760. //获取手机号
  761. if (this.$isEmpty(this.vuex_phone)) {
  762. this.showPhoneModal()
  763. }
  764. },
  765. // 登录 end
  766. /**
  767. * u-view 菜单栏 begin
  768. *
  769. */
  770. async swichMenu(index) {
  771. if (this.arr.length == 0) {
  772. await this.getMenuItemTop();
  773. }
  774. if (index == this.current) return;
  775. this.scrollRightTop = this.oldScrollTop;
  776. this.$nextTick(function() {
  777. this.scrollRightTop = this.arr[index];
  778. this.current = index;
  779. this.leftMenuStatus(index);
  780. })
  781. },
  782. // 获取一个目标元素的高度
  783. getElRect(elClass, dataVal) {
  784. new Promise((resolve, reject) => {
  785. const query = uni.createSelectorQuery().in(this);
  786. query.select('.' + elClass).fields({
  787. size: true
  788. }, res => {
  789. // 如果节点尚未生成,res值为null,循环调用执行
  790. if (!res) {
  791. setTimeout(() => {
  792. this.getElRect(elClass);
  793. }, 10);
  794. return;
  795. }
  796. this[dataVal] = res.height;
  797. resolve();
  798. }).exec();
  799. })
  800. },
  801. // 观测元素相交状态
  802. async observer() {
  803. this.goods.map((val, index) => {
  804. let observer = uni.createIntersectionObserver(this);
  805. // 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
  806. // 如果跟.right-box底部相交,就动态设置左边栏目的活动状态
  807. observer.relativeTo('.right-box', {
  808. top: 0
  809. }).observe('#item' + index, res => {
  810. if (res.intersectionRatio > 0) {
  811. let id = res.id.substring(4);
  812. this.leftMenuStatus(id);
  813. }
  814. })
  815. })
  816. },
  817. // 设置左边菜单的滚动状态
  818. async leftMenuStatus(index) {
  819. this.current = index;
  820. // 如果为0,意味着尚未初始化
  821. if (this.menuHeight == 0 || this.menuItemHeight == 0) {
  822. await this.getElRect('menu-scroll-view', 'menuHeight');
  823. await this.getElRect('u-tab-item', 'menuItemHeight');
  824. }
  825. // 将菜单活动item垂直居中
  826. this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
  827. },
  828. // 获取右边菜单每个item到顶部的距离
  829. getMenuItemTop() {
  830. new Promise(resolve => {
  831. let selectorQuery = uni.createSelectorQuery();
  832. selectorQuery.selectAll('.class-item').boundingClientRect((rects) => {
  833. // 如果节点尚未生成,rects值为[](因为用selectAll,所以返回的是数组),循环调用执行
  834. if (!rects.length) {
  835. setTimeout(() => {
  836. this.getMenuItemTop();
  837. }, 10);
  838. return;
  839. }
  840. rects.forEach((rect) => {
  841. // 这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
  842. this.arr.push(rect.top - rects[0].top);
  843. resolve();
  844. })
  845. }).exec()
  846. })
  847. },
  848. // 右边菜单滚动
  849. async rightScroll(e) {
  850. this.oldScrollTop = e.detail.scrollTop;
  851. if (this.arr.length == 0) {
  852. await this.getMenuItemTop();
  853. }
  854. if (this.timer) return;
  855. if (!this.menuHeight) {
  856. await this.getElRect('menu-scroll-view', 'menuHeight');
  857. }
  858. setTimeout(() => { // 节流
  859. this.timer = null;
  860. // scrollHeight为右边菜单垂直中点位置
  861. let scrollHeight = e.detail.scrollTop + this.menuHeight / 2;
  862. for (let i = 0; i < this.arr.length; i++) {
  863. let height1 = this.arr[i];
  864. let height2 = this.arr[i + 1];
  865. // 如果不存在height2,意味着数据循环已经到了最后一个,设置左边菜单为最后一项即可
  866. if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
  867. this.leftMenuStatus(i);
  868. return;
  869. }
  870. }
  871. }, 10)
  872. },
  873. /**
  874. * u-view 菜单栏 end
  875. */
  876. }
  877. }
  878. </script>
  879. <style lang="scss" scoped>
  880. .goods_empty {
  881. display: flex;
  882. justify-content: center;
  883. align-items: center;
  884. flex-direction: column;
  885. margin-top: 300rpx;
  886. image {
  887. width: 270rpx;
  888. height: 160rpx;
  889. margin-bottom: 50rpx;
  890. }
  891. text: {
  892. font-weight: 400;
  893. color: #DDDDDD;
  894. }
  895. }
  896. .nav {
  897. width: 100%;
  898. flex-shrink: 0;
  899. display: flex;
  900. flex-direction: column;
  901. .header {
  902. width: 100%;
  903. display: flex;
  904. align-items: center;
  905. justify-content: space-between;
  906. padding: 20rpx;
  907. background-color: #ffffff;
  908. height: 100rpx;
  909. .left {
  910. flex: 1;
  911. display: flex;
  912. flex-direction: column;
  913. .store-name {
  914. display: flex;
  915. justify-content: flex-start;
  916. align-items: center;
  917. font-size: $font-size-lg;
  918. margin-bottom: 10rpx;
  919. .iconfont {
  920. margin-left: 10rpx;
  921. line-height: 100%;
  922. }
  923. }
  924. .store-location {
  925. display: flex;
  926. justify-content: flex-start;
  927. align-items: center;
  928. color: $text-color-assist;
  929. font-size: $font-size-sm;
  930. .iconfont {
  931. vertical-align: middle;
  932. display: table-cell;
  933. color: $color-primary;
  934. line-height: 100%;
  935. }
  936. }
  937. }
  938. .right {
  939. background-color: $bg-color-grey;
  940. border-radius: 38rpx;
  941. display: flex;
  942. align-items: center;
  943. font-size: $font-size-sm;
  944. padding: 0 38rpx;
  945. color: $text-color-assist;
  946. .dinein,
  947. .takeout {
  948. position: relative;
  949. display: flex;
  950. align-items: center;
  951. &.active {
  952. padding: 14rpx 38rpx;
  953. color: #ffffff;
  954. background-color: $color-primary;
  955. border-radius: 38rpx;
  956. }
  957. }
  958. .takeout {
  959. margin-left: 20rpx;
  960. height: 100%;
  961. flex: 1;
  962. padding: 14rpx 0;
  963. }
  964. .dinein.active {
  965. margin-left: -38rpx;
  966. }
  967. .takeout.active {
  968. margin-right: -38rpx;
  969. }
  970. }
  971. }
  972. .coupon {
  973. flex: 1;
  974. width: 100%;
  975. font-size: $font-size-base;
  976. color: $color-primary;
  977. padding: 0 20rpx;
  978. display: flex;
  979. align-items: center;
  980. overflow: hidden;
  981. .title {
  982. flex: 1;
  983. margin-left: 10rpx;
  984. overflow: hidden;
  985. white-space: nowrap;
  986. text-overflow: ellipsis;
  987. }
  988. .iconfont {
  989. line-height: 100%;
  990. }
  991. }
  992. }
  993. .item-container {
  994. display: flex;
  995. flex-wrap: wrap;
  996. .thumb-box {
  997. width: 100%;
  998. display: flex;
  999. padding: 20rpx 0;
  1000. border-bottom: 1rpx solid #EEEEEE;
  1001. .item-menu-image {
  1002. width: 160rpx;
  1003. height: 160rpx;
  1004. margin-right: 20rpx;
  1005. border-radius: 8rpx;
  1006. }
  1007. .item-content {
  1008. width: calc(100% - 180rpx);
  1009. }
  1010. .name {
  1011. font-size: $font-size-base;
  1012. margin-bottom: 10rpx;
  1013. }
  1014. .tips {
  1015. width: 100%;
  1016. height: 40rpx;
  1017. line-height: 40rpx;
  1018. overflow: hidden;
  1019. text-overflow: ellipsis;
  1020. white-space: nowrap;
  1021. font-size: $font-size-sm;
  1022. color: $text-color-assist;
  1023. margin-bottom: 10rpx;
  1024. }
  1025. .price_and_action {
  1026. width: 100%;
  1027. display: flex;
  1028. justify-content: space-between;
  1029. align-items: center;
  1030. margin-top: 30rpx;
  1031. .price {
  1032. font-size: $font-size-base;
  1033. font-weight: 600;
  1034. }
  1035. .btn-group {
  1036. display: flex;
  1037. justify-content: space-between;
  1038. align-items: center;
  1039. position: relative;
  1040. .btn {
  1041. background-color: $base-color;
  1042. padding: 0 20rpx;
  1043. box-sizing: border-box;
  1044. font-size: $font-size-sm;
  1045. height: 44rpx;
  1046. line-height: 44rpx;
  1047. &.property_btn {
  1048. border-radius: 24rpx;
  1049. }
  1050. &.add_btn,
  1051. &.reduce_btn {
  1052. color: #FFFFFF;
  1053. border: $base-color;
  1054. padding: 0;
  1055. width: 44rpx;
  1056. border-radius: 44rpx;
  1057. }
  1058. }
  1059. .dot {
  1060. position: absolute;
  1061. background-color: #ffffff;
  1062. border: 1px solid $color-primary;
  1063. color: $color-primary;
  1064. font-size: $font-size-sm;
  1065. width: 36rpx;
  1066. height: 36rpx;
  1067. line-height: 36rpx;
  1068. text-align: center;
  1069. border-radius: 100%;
  1070. right: -12rpx;
  1071. top: -10rpx;
  1072. }
  1073. .number {
  1074. width: 44rpx;
  1075. height: 44rpx;
  1076. line-height: 44rpx;
  1077. text-align: center;
  1078. }
  1079. }
  1080. }
  1081. }
  1082. .thumb-box:last-child {
  1083. border: none;
  1084. }
  1085. }
  1086. .good-detail-modal {
  1087. width: 100%;
  1088. height: 100%;
  1089. display: flex;
  1090. flex-direction: column;
  1091. .cover {
  1092. height: 320rpx;
  1093. padding: 30rpx 0;
  1094. display: flex;
  1095. justify-content: center;
  1096. align-items: center;
  1097. position: relative;
  1098. .image {
  1099. width: 260rpx;
  1100. height: 260rpx;
  1101. }
  1102. .btn-group {
  1103. position: absolute;
  1104. right: 10rpx;
  1105. top: 30rpx;
  1106. display: flex;
  1107. align-items: center;
  1108. justify-content: space-around;
  1109. image {
  1110. width: 80rpx;
  1111. height: 80rpx;
  1112. }
  1113. }
  1114. }
  1115. .detail {
  1116. width: 100%;
  1117. min-height: 1vh;
  1118. max-height: calc(90vh - 320rpx - 80rpx - 330rpx);
  1119. .wrapper {
  1120. width: 100%;
  1121. height: 100%;
  1122. overflow: hidden;
  1123. .basic {
  1124. padding: 0 20rpx 30rpx;
  1125. display: flex;
  1126. flex-direction: column;
  1127. .name {
  1128. font-size: $font-size-base;
  1129. color: $text-color-base;
  1130. margin-bottom: 10rpx;
  1131. }
  1132. .tips {
  1133. font-size: $font-size-sm;
  1134. color: $text-color-grey;
  1135. }
  1136. }
  1137. .properties {
  1138. width: 100%;
  1139. border-top: 2rpx solid $bg-color-grey;
  1140. padding: 10rpx 30rpx 0;
  1141. display: flex;
  1142. flex-direction: column;
  1143. .property {
  1144. width: 100%;
  1145. display: flex;
  1146. flex-direction: column;
  1147. margin-bottom: 20rpx;
  1148. padding-bottom: -16rpx;
  1149. .title {
  1150. width: 100%;
  1151. display: flex;
  1152. justify-content: flex-start;
  1153. align-items: center;
  1154. margin-bottom: 20rpx;
  1155. .name {
  1156. font-size: 26rpx;
  1157. color: $text-color-base;
  1158. margin-right: 20rpx;
  1159. }
  1160. .desc {
  1161. flex: 1;
  1162. font-size: $font-size-sm;
  1163. color: $color-primary;
  1164. overflow: hidden;
  1165. text-overflow: ellipsis;
  1166. white-space: nowrap;
  1167. }
  1168. }
  1169. .values {
  1170. width: 100%;
  1171. display: flex;
  1172. flex-wrap: wrap;
  1173. .value {
  1174. border-radius: 8rpx;
  1175. background-color: $bg-color-grey;
  1176. padding: 16rpx 30rpx;
  1177. font-size: 26rpx;
  1178. color: $text-color-assist;
  1179. margin-right: 16rpx;
  1180. margin-bottom: 16rpx;
  1181. &.default {
  1182. background-color: $color-primary;
  1183. color: $text-color-white;
  1184. }
  1185. }
  1186. }
  1187. }
  1188. }
  1189. }
  1190. }
  1191. .action {
  1192. display: flex;
  1193. align-items: center;
  1194. justify-content: space-between;
  1195. background-color: $bg-color-grey;
  1196. height: 120rpx;
  1197. padding: 0 26rpx;
  1198. .left {
  1199. flex: 1;
  1200. display: flex;
  1201. flex-direction: column;
  1202. justify-content: center;
  1203. margin-right: 20rpx;
  1204. overflow: hidden;
  1205. .price {
  1206. font-size: $font-size-lg;
  1207. color: $text-color-base;
  1208. }
  1209. .props {
  1210. color: $text-color-assist;
  1211. font-size: 24rpx;
  1212. width: 100%;
  1213. overflow: hidden;
  1214. text-overflow: ellipsis;
  1215. white-space: nowrap;
  1216. }
  1217. }
  1218. .btn-group {
  1219. display: flex;
  1220. align-items: center;
  1221. justify-content: space-around;
  1222. .number {
  1223. font-size: $font-size-base;
  1224. width: 44rpx;
  1225. height: 44rpx;
  1226. line-height: 44rpx;
  1227. text-align: center;
  1228. }
  1229. .btn {
  1230. padding: 0;
  1231. font-size: $font-size-base;
  1232. width: 44rpx;
  1233. height: 44rpx;
  1234. line-height: 44rpx;
  1235. border-radius: 100%;
  1236. }
  1237. }
  1238. }
  1239. .add-to-cart-btn {
  1240. display: flex;
  1241. justify-content: center;
  1242. align-items: center;
  1243. background-color: $color-primary;
  1244. color: $text-color-white;
  1245. font-size: $font-size-base;
  1246. height: 80rpx;
  1247. border-radius: 0 0 12rpx 12rpx;
  1248. }
  1249. }
  1250. .cart-popup {
  1251. .top {
  1252. background-color: #f3ede3;
  1253. color: #ff9447;
  1254. padding: 10rpx 30rpx;
  1255. font-size: 24rpx;
  1256. text-align: right;
  1257. }
  1258. .cart-list {
  1259. background-color: #FFFFFF;
  1260. width: 100%;
  1261. overflow: hidden;
  1262. min-height: 30vh;
  1263. max-height: 70vh;
  1264. margin-bottom: 30rpx;
  1265. .wrapper {
  1266. height: 100%;
  1267. display: flex;
  1268. flex-direction: column;
  1269. padding: 0 30rpx;
  1270. margin-bottom: 156rpx;
  1271. .item {
  1272. display: flex;
  1273. justify-content: space-between;
  1274. align-items: center;
  1275. padding: 30rpx 0;
  1276. position: relative;
  1277. &::after {
  1278. content: ' ';
  1279. position: absolute;
  1280. bottom: 0;
  1281. left: 0;
  1282. width: 100%;
  1283. background-color: $border-color;
  1284. height: 2rpx;
  1285. transform: scaleY(.6);
  1286. }
  1287. .left {
  1288. flex: 1;
  1289. display: flex;
  1290. flex-direction: column;
  1291. overflow: hidden;
  1292. margin-right: 30rpx;
  1293. .name {
  1294. font-size: $font-size-sm;
  1295. color: $text-color-base;
  1296. }
  1297. .props {
  1298. color: $text-color-assist;
  1299. font-size: 24rpx;
  1300. overflow: hidden;
  1301. text-overflow: ellipsis;
  1302. white-space: nowrap;
  1303. }
  1304. }
  1305. .center {
  1306. margin-right: 120rpx;
  1307. font-size: $font-size-base;
  1308. }
  1309. .right {
  1310. display: flex;
  1311. align-items: center;
  1312. justify-content: space-between;
  1313. .btn {
  1314. width: 46rpx;
  1315. height: 46rpx;
  1316. border-radius: 100%;
  1317. padding: 0;
  1318. text-align: center;
  1319. line-height: 46rpx;
  1320. }
  1321. .number {
  1322. font-size: $font-size-base;
  1323. width: 46rpx;
  1324. height: 46rpx;
  1325. text-align: center;
  1326. line-height: 46rpx;
  1327. }
  1328. }
  1329. }
  1330. }
  1331. }
  1332. }
  1333. .cart-box {
  1334. position: fixed;
  1335. bottom: 30rpx;
  1336. left: 30rpx;
  1337. right: 30rpx;
  1338. height: 96rpx;
  1339. border-radius: 48rpx;
  1340. box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.2);
  1341. background-color: #FFFFFF;
  1342. display: flex;
  1343. align-items: center;
  1344. justify-content: space-between;
  1345. z-index: 999;
  1346. .cart-img {
  1347. width: 96rpx;
  1348. height: 96rpx;
  1349. position: relative;
  1350. margin-top: -48rpx;
  1351. }
  1352. .pay-btn {
  1353. height: 100%;
  1354. padding: 0 30rpx;
  1355. color: #FFFFFF;
  1356. border-radius: 0 50rpx 50rpx 0;
  1357. display: flex;
  1358. align-items: center;
  1359. background-color: rgba(255, 148, 71, .6);
  1360. font-size: $font-size-base;
  1361. }
  1362. .mark {
  1363. padding-left: 46rpx;
  1364. margin-right: 30rpx;
  1365. position: relative;
  1366. .tag {
  1367. background-color: #ED302C;
  1368. color: #fff;
  1369. border: 1rpx solid #ED302C;
  1370. display: flex;
  1371. justify-content: center;
  1372. align-items: center;
  1373. font-size: $font-size-sm;
  1374. position: absolute;
  1375. right: -10rpx;
  1376. top: -50rpx;
  1377. border-radius: 100%;
  1378. padding: 4rpx;
  1379. width: 36rpx;
  1380. height: 36rpx;
  1381. opacity: .9;
  1382. }
  1383. }
  1384. .price {
  1385. flex: 1;
  1386. color: $text-color-base;
  1387. }
  1388. }
  1389. .u-wrap {
  1390. height: calc(100vh);
  1391. /* #ifdef H5 */
  1392. height: calc(100vh - var(--window-top));
  1393. /* #endif */
  1394. display: flex;
  1395. flex-direction: column;
  1396. width: 100%;
  1397. }
  1398. .u-search-box {
  1399. background-color: #FFFFFF;
  1400. }
  1401. .u-menu-wrap {
  1402. width: 100%;
  1403. flex: 1;
  1404. display: flex;
  1405. overflow: hidden;
  1406. }
  1407. .u-search-inner {
  1408. background-color: rgb(234, 234, 234);
  1409. border-radius: 100rpx;
  1410. display: flex;
  1411. align-items: center;
  1412. padding: 10rpx 16rpx;
  1413. }
  1414. .u-search-text {
  1415. font-size: 26rpx;
  1416. color: $u-tips-color;
  1417. margin-left: 10rpx;
  1418. }
  1419. .u-tab-view {
  1420. width: 200rpx;
  1421. height: 100%;
  1422. }
  1423. .u-tab-item {
  1424. height: 110rpx;
  1425. background: #f6f6f6;
  1426. box-sizing: border-box;
  1427. display: flex;
  1428. align-items: center;
  1429. justify-content: center;
  1430. font-size: 26rpx;
  1431. color: #444;
  1432. font-weight: 400;
  1433. line-height: 1;
  1434. }
  1435. .u-tab-item-active {
  1436. position: relative;
  1437. color: #000;
  1438. font-size: 30rpx;
  1439. font-weight: 600;
  1440. background: #fff;
  1441. }
  1442. .u-tab-item-active::before {
  1443. content: "";
  1444. position: absolute;
  1445. border-left: 4px solid $base-color;
  1446. height: 32rpx;
  1447. left: 0;
  1448. top: 39rpx;
  1449. }
  1450. .u-tab-view {
  1451. height: 100%;
  1452. }
  1453. .right-box {
  1454. background-color: rgb(250, 250, 250);
  1455. }
  1456. .page-view {
  1457. margin-bottom: 80rpx;
  1458. background-color: #FFFFFF;
  1459. padding: 16rpx;
  1460. }
  1461. .class-item {
  1462. margin-bottom: 30rpx;
  1463. background-color: #fff;
  1464. border-radius: 8rpx;
  1465. }
  1466. .class-item:first-child {
  1467. margin-bottom: 0rpx;
  1468. }
  1469. .class-item:last-child {
  1470. min-height: 100vh;
  1471. }
  1472. .item-title {
  1473. padding: 20rpx;
  1474. font-size: 26rpx;
  1475. color: $u-main-color;
  1476. font-weight: bold;
  1477. }
  1478. </style>