menu.vue 41 KB

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