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