index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. <template>
  2. <view class="content">
  3. <image class="bg" src="@/static/images/bg.png" mode="aspectFill"></image>
  4. <view class="header-wrap">
  5. <SearchBar
  6. :margin-top="'24rpx'"
  7. :bg-color="bgColor"
  8. :status-bar-height="statusBarHeight"
  9. :cart-count="cartCount"
  10. :search-width="'100%'"
  11. placeholder="搜索商品"
  12. @searchClick="toSearch"
  13. @cartClick="goAuthUrl()"
  14. />
  15. </view>
  16. <scroll-view scroll-y class="scroll-content" @scroll="onScroll" @scrolltolower="loadMoreGoods">
  17. <!-- 顶部占位:避免固定搜索栏遮挡 -->
  18. <view style="padding-bottom:110rpx">
  19. <view class="status_bar" :style="{height: statusBarHeight}"></view>
  20. </view>
  21. <!-- 推荐频道 -->
  22. <ChannelEntry :list="channelList" :per-row="4" :rows="2" @click="onChannelClick" />
  23. <!-- 金刚区:分类图标 2 行 4 列,仅展示 -->
  24. <CategoryTags :tags="categoryTagsData" @selectClick="onCategoryTagsSelect" />
  25. <!-- 推荐区块:左侧直播卡(直播中/回放)+ 右上绿色有机 + 右下上新推荐 -->
  26. <RecommendSection
  27. :live="recommendData.live"
  28. :green="recommendData.green"
  29. :hot="recommendData.hot"
  30. @liveClick="onLiveClick"
  31. @more="onRecommendMore"
  32. @itemClick="onRecommendItemClick"
  33. />
  34. <!-- 瀑布流:全部 + 分类标签横向滚动 -->
  35. <GoodsNav :nav-list="goodsNavList" :active-id="activeGoodsNavId" @select="onGoodsNavSelect" />
  36. <GoodsList
  37. :list="goodsList"
  38. :loading="goodsLoading"
  39. :has-more="goodsHasMore"
  40. @itemClick="showProduct"
  41. @loadMore="loadMoreGoods"
  42. />
  43. <!--#ifdef MP-WEIXIN-->
  44. <view class="official-account">
  45. <official-account @load="bindload" @error="binderror"></official-account>
  46. </view>
  47. <!--#endif-->
  48. <view class="popup-box" v-if="activityShow">
  49. <view class="info-mask" @tap="closeActivity()"></view>
  50. <view class="info-form">
  51. <image :src="activity.logoUrl" @tap="showActivity()" />
  52. </view>
  53. </view>
  54. <!-- <Server /> -->
  55. </scroll-view>
  56. </view>
  57. </template>
  58. <script>
  59. // import zModal from '@/components/z-modal/z-modal.vue'
  60. import { getMenu, getIndexData, getCartCount } from '@/api/index'
  61. import { getStoreConfig } from '@/api/common'
  62. import { getHomeInit, getHomeRecommend, getHomeGoods } from '@/api/home.js'
  63. import HotProduct from './components/HotProduct.vue'
  64. //import TuiProduct from '@/components/tuiProduct.vue'
  65. import SearchBar from './components/SearchBar.vue'
  66. import CategoryTags from './components/CategoryTags.vue'
  67. import ChannelEntry from './components/ChannelEntry.vue'
  68. import RecommendSection from './components/RecommendSection.vue'
  69. import GoodsNav from './components/GoodsNav.vue'
  70. import GoodsList from './components/GoodsList.vue'
  71. import { getUserInfo, bindPromoter } from '@/api/user'
  72. export default {
  73. components: {
  74. // zModal,
  75. HotProduct,
  76. //TuiProduct,
  77. SearchBar,
  78. CategoryTags,
  79. ChannelEntry,
  80. RecommendSection,
  81. GoodsNav,
  82. GoodsList
  83. },
  84. data() {
  85. return {
  86. btnGroup: [
  87. { text: '取消', color: '#FFFFFF', bgColor: '#999999', width: '150rpx', height: '80rpx', shape: 'fillet', eventName: 'cancle' },
  88. { text: '确定', color: '#FFFFFF', bgColor: '#FF233C', width: '150rpx', height: '80rpx', shape: 'fillet', eventName: 'sure' }
  89. ],
  90. menuButtonInfo: {
  91. top: '0px',
  92. height: '0px',
  93. centerY: '0px',
  94. right: '0px'
  95. }, // 胶囊按钮布局信息
  96. tuiModalControl: false,
  97. activity: null,
  98. activityShow: false,
  99. hotProductList: [],
  100. menus: [],
  101. channelList: [],
  102. categoryTagsData: [],
  103. goodsNav: [],
  104. recommendData: { live: [], green: [], hot: [] },
  105. activeGoodsNavId: 'all',
  106. goodsList: [],
  107. goodsLoading: false,
  108. goodsHasMore: true,
  109. goodsPageNum: 1,
  110. cartCount: 0,
  111. advList: [],
  112. top: 0,
  113. statusBarHeight: uni.getSystemInfoSync().statusBarHeight + 'px',
  114. userinfoa: [],
  115. isuser: false,
  116. currentTab: 0
  117. }
  118. },
  119. computed: {
  120. bgColor() {
  121. const t = this.top / 30
  122. if(t>0){
  123. return '/static/images/bg.png'
  124. }else{
  125. return ''
  126. }
  127. },
  128. // 瀑布流标签:后端 categoryTags(不包含“全部”)
  129. goodsNavList() {
  130. const tags = (this.goodsNav || []).map(t => ({
  131. id: t.id || t.cateId,
  132. name: t.categoryName || t.cateName || t.name,
  133. categoryName: t.categoryName || t.cateName || t.name
  134. }))
  135. return tags
  136. }
  137. },
  138. onLoad(option) {
  139. this.getMenuButtonInfo()
  140. if (option.userCode != null) {
  141. uni.setStorageSync('userCode', option.userCode)
  142. if (this.$isLogin()) this.getUserInfo()
  143. }
  144. if (option.hasOwnProperty('q') && option.q) {
  145. const url = decodeURIComponent(option.q)
  146. const obj = this.$urlToObj(url)
  147. uni.setStorageSync('userCode', obj.userCode)
  148. if (this.$isLogin()) this.getUserInfo()
  149. }
  150. },
  151. onUnload() {
  152. uni.$emit('stop')
  153. },
  154. onHide() {
  155. uni.$emit('stop')
  156. },
  157. onShow() {
  158. this.initIndexData()
  159. if (this.$isLogin()) this.getCartCount()
  160. },
  161. onShareAppMessage() {
  162. return { title: '岚财优选-您的专属解决方案', path: '/pages/common/launch', imageUrl: '/static/logo.jpg' }
  163. },
  164. onShareTimeline() {
  165. return { title: '岚财优选-您的专属解决方案', query: '', imageUrl: '/static/logo.jpg' }
  166. },
  167. methods: {
  168. onScroll(e) {
  169. // scroll-view 的滚动事件
  170. this.top = e?.detail?.scrollTop ?? e?.scrollTop ?? 0
  171. },
  172. switchTab(index) {
  173. this.currentTab = index;
  174. },
  175. initIndexData() {
  176. this.getHomeInit()
  177. this.getHomeRecommend()
  178. this.getMenu()
  179. if (uni.getStorageSync('AppToken')) {
  180. this.getUserInfo()
  181. } else {
  182. this.isuser = true
  183. }
  184. this.getStoreConfig()
  185. },
  186. // 获取胶囊按钮布局参数
  187. getMenuButtonInfo() {
  188. // 微信小程序API(Uniapp可直接用uni.getMenuButtonBoundingClientRect)
  189. const menuBtn = uni.getMenuButtonBoundingClientRect();
  190. if (menuBtn) {
  191. this.menuButtonInfo = {
  192. top: menuBtn.top + 'px', // 胶囊顶部距离
  193. height: menuBtn.height + 'px', // 胶囊高度
  194. centerY: (menuBtn.top + menuBtn.height / 2) + 'px', // 胶囊垂直居中Y坐标
  195. right: menuBtn.right + 'px' // 胶囊右侧距离
  196. };
  197. }
  198. },
  199. getHomeInit() {
  200. getHomeInit().then(res => {
  201. if (res.code == 200 && res.data) {
  202. this.channelList = res.data.channelList || []
  203. this.categoryTagsData = res.data.categoryTags || []
  204. this.goodsNav = res.data.goodsNav || []
  205. if (this.activeGoodsNavId === null || this.activeGoodsNavId === undefined) {
  206. this.activeGoodsNavId = 'all'
  207. }
  208. this.fetchGoodsList(true)
  209. }
  210. }).catch(() => {})
  211. },
  212. getHomeRecommend() {
  213. getHomeRecommend().then(res => {
  214. if (res.code != 200) return
  215. this.recommendData = {
  216. live: res.live && Array.isArray(res.live) ? res.live.slice(0, 3) : [],
  217. green: res.green && Array.isArray(res.green) ? res.green : [],
  218. hot: res.hot && Array.isArray(res.hot) ? res.hot : []
  219. }
  220. }).catch(() => {})
  221. },
  222. fetchGoodsList(reset) {
  223. if (reset) {
  224. this.goodsPageNum = 1
  225. this.goodsList = []
  226. this.goodsHasMore = true
  227. }
  228. if (this.goodsLoading || !this.goodsHasMore) return
  229. this.goodsLoading = true
  230. const id = this.activeGoodsNavId === 'all' || this.activeGoodsNavId === '' ? 0 : this.activeGoodsNavId
  231. getHomeGoods({ id, position:2, pageNum: this.goodsPageNum, pageSize: 10 }).then(res => {
  232. this.goodsLoading = false
  233. if (res.code == 200 && res.data) {
  234. const list = Array.isArray(res.data.list) ? res.data.list : []
  235. const total = res.data.total != null ? Number(res.data.total) : 0
  236. if (reset) this.goodsList = list
  237. else this.goodsList = this.goodsList.concat(list)
  238. this.goodsHasMore = list.length >= 20 && (this.goodsList.length < total || total === 0)
  239. this.goodsPageNum++
  240. }
  241. }).catch(() => { this.goodsLoading = false })
  242. },
  243. loadMoreGoods() {
  244. this.fetchGoodsList(false)
  245. },
  246. onGoodsNavSelect(item) {
  247. //console.log("2222")
  248. //console.log(item.id)
  249. this.activeGoodsNavId = item.id
  250. this.fetchGoodsList(true)
  251. },
  252. onCategoryTagsSelect(item) {
  253. //console.log(this.categoryTagsData,'item')
  254. // 金刚区点击:带分类 id 跳转商品列表页筛选(防护 item 为空)
  255. if (!item || typeof item !== 'object') {
  256. uni.navigateTo({ url: '/pages_mall/productList' })
  257. return
  258. }
  259. const id = item.id ?? item.cateId ?? item.categoryId
  260. if (id != null && id !== '') {
  261. uni.navigateTo({ url: '/pages_mall/productList?id=' + id })
  262. } else {
  263. uni.navigateTo({ url: '/pages_mall/productList' })
  264. }
  265. },
  266. onChannelClick(item) {
  267. // 推荐频道点击,可跳转或筛选,按需扩展
  268. },
  269. onRecommendMore(section) {
  270. if (section && section.moreUrl) {
  271. uni.navigateTo({ url: '/pages_mall/recommendList' })
  272. return
  273. }
  274. if (section && (section.type === 'green' || section.type === 'hot')) {
  275. uni.navigateTo({ url: '/pages_mall/recommendList?type='+section.type})
  276. }
  277. },
  278. onLiveClick(item) {
  279. //console.log(item)
  280. if (item && item.liveId) uni.navigateTo({ url: '/pages_course/living?liveId=' + item.liveId })
  281. },
  282. onRecommendItemClick(item,type) {
  283. if (item.productId) this.showProduct(item,type)
  284. },
  285. getMenu() {
  286. // getMenu().then(res => {
  287. // if (res.code == 200) this.menus = res.data || []
  288. // })
  289. },
  290. getIndexData() {
  291. getIndexData({}).then(res => {
  292. if (res.code == 200) {
  293. this.advList = res.data.advList || []
  294. this.hotProductList = res.data.hotProductList || []
  295. } else {
  296. uni.showToast({ icon: 'none', title: '请求失败' })
  297. }
  298. })
  299. },
  300. getUserInfo() {
  301. getUserInfo().then(res => {
  302. if (res.code == 200 && res.user != null) this.userinfoa = res.user
  303. else uni.showToast({ icon: 'none', title: '请求失败' })
  304. })
  305. },
  306. getCartCount() {
  307. if (this.$isLogin()){
  308. getCartCount().then(cartRes => {
  309. if (cartRes.code == 200) this.cartCount = cartRes.data
  310. })
  311. }
  312. },
  313. getStoreConfig() {
  314. getStoreConfig().then(res => {
  315. if (res.code == 200) uni.setStorageSync('config', JSON.stringify(res.data))
  316. })
  317. },
  318. cancleTui() {
  319. this.tuiModalControl = false
  320. },
  321. submitTui(e) {
  322. if (!e.inputText) {
  323. uni.showToast({ icon: 'none', title: '请输入邀请码' })
  324. return
  325. }
  326. bindPromoter({ userCode: e.inputText }).then(res => {
  327. uni.showToast({ icon: 'none', title: res.msg })
  328. if (res.code == 200) this.tuiModalControl = false
  329. })
  330. },
  331. bindload() {},
  332. binderror() {},
  333. closeActivity() {
  334. this.activityShow = false
  335. },
  336. showActivity() {
  337. this.activityShow = false
  338. if (this.activity && this.activity.activityId) {
  339. uni.navigateTo({ url: '/pages_shopping/shopping/activityDetails?activityId=' + this.activity.activityId })
  340. }
  341. },
  342. menuClick(item) {
  343. const linkUrl = item.linkUrl || item.url
  344. const linkType = item.linkType != null ? item.linkType : (linkUrl ? 1 : 0)
  345. if (linkType == 1 && linkUrl) {
  346. if (linkUrl == '/pages/shopping/index' || linkUrl == '/pages/healthy/index') {
  347. uni.switchTab({ url: linkUrl })
  348. } else {
  349. uni.navigateTo({ url: linkUrl })
  350. }
  351. } else if (linkType == 0) {
  352. uni.showToast({ icon: 'none', title: '开发中...' })
  353. }
  354. },
  355. handleAdvClick(item) {
  356. if (item.showType == 1) {
  357. uni.setStorageSync('url', item.advUrl)
  358. uni.navigateTo({ url: 'h5' })
  359. } else if (item.showType == 2) {
  360. uni.navigateTo({ url: item.advUrl })
  361. } else if (item.showType == 3) {
  362. uni.setStorageSync('content', item.content)
  363. uni.navigateTo({ url: 'content' })
  364. }
  365. },
  366. goAuthUrl(url) {
  367. if (this.$isLogin()) {
  368. uni.switchTab({ url })
  369. }
  370. },
  371. navTo(url) {
  372. uni.navigateTo({ url })
  373. },
  374. toSearch() {
  375. //uni.navigateTo({ url: './productSearch' })
  376. uni.navigateTo({ url: '/pages_mall/productList' })
  377. },
  378. showProduct(item,type) {
  379. //console.log('item',type)
  380. //uni.navigateTo({ url: '../shopping/productDetails?productId=' + item.productId })
  381. uni.navigateTo({ url: '/pages_mall/recommendList?type='+type})
  382. }
  383. }
  384. }
  385. </script>
  386. <style lang="scss" scoped>
  387. .tab-bar {
  388. height: 100rpx;
  389. background-color: #fff;
  390. display: flex;
  391. align-items: center;
  392. justify-content: space-around;
  393. border-top: 1rpx solid #eee;
  394. padding-bottom: constant(safe-area-inset-bottom);
  395. padding-bottom: env(safe-area-inset-bottom);
  396. position: fixed;
  397. bottom: 0;
  398. left: 0;
  399. right: 0;
  400. z-index: 9999;
  401. .tab-item {
  402. display: flex;
  403. flex-direction: column;
  404. align-items: center;
  405. justify-content: center;
  406. text {
  407. font-size: 24rpx;
  408. margin-top: 6rpx;
  409. color: #999999;
  410. }
  411. &.active text {
  412. color: #FF233C;
  413. }
  414. .icon-wrap {
  415. position: relative;
  416. .badge {
  417. position: absolute;
  418. top: -10rpx;
  419. right: -10rpx;
  420. background: #fa436a;
  421. color: #fff;
  422. font-size: 18rpx;
  423. width: 30rpx;
  424. height: 30rpx;
  425. border-radius: 50%;
  426. display: flex;
  427. align-items: center;
  428. justify-content: center;
  429. }
  430. }
  431. }
  432. }
  433. .content {
  434. width: 100%;
  435. position: relative;
  436. min-height: 0;
  437. padding-top: 1rpx;
  438. display: flex;
  439. flex-direction: column;
  440. height: 100%;
  441. }
  442. .scroll-content {
  443. flex: 1;
  444. height: 0;
  445. }
  446. .header-wrap {
  447. position: relative;
  448. z-index: 10;
  449. }
  450. .banner-row {
  451. position: relative;
  452. display: flex;
  453. gap: 20rpx;
  454. padding: 20rpx 24rpx;
  455. // background: #fff;
  456. }
  457. .banner-item {
  458. flex: 1;
  459. height: 220rpx;
  460. border-radius: 16rpx;
  461. overflow: hidden;
  462. background: #f5f5f5;
  463. }
  464. .banner-img {
  465. width: 100%;
  466. height: 100%;
  467. }
  468. .bg {
  469. width: 100%;
  470. height: 380rpx;
  471. position: absolute;
  472. top: 0;
  473. left: 0;
  474. z-index: 0;
  475. }
  476. .status_bar {
  477. width: 100%;
  478. }
  479. .banner-box {
  480. padding: 0 20upx;
  481. }
  482. .banner-box .inner {
  483. width: 100%;
  484. height: 236upx;
  485. border-radius: 10upx;
  486. overflow: hidden;
  487. }
  488. .banner-box .swiper,
  489. .banner-box .swiper-item,
  490. .banner-box .swiper-item image {
  491. width: 100%;
  492. height: 100%;
  493. }
  494. .menu-content {
  495. background-color: #fff;
  496. overflow: hidden;
  497. padding: 20upx 20upx 0;
  498. }
  499. .menu-box {
  500. display: flex;
  501. align-items: center;
  502. background-color: #fff;
  503. }
  504. .online-inquiry {
  505. box-sizing: border-box;
  506. width: 100%;
  507. height: 300upx;
  508. padding: 20upx;
  509. background: linear-gradient(180deg, rgba(255, 255, 255, 0.38) 62%, rgba(255, 255, 255, 0) 100%);
  510. }
  511. .online-inquiry .item {
  512. width: 100%;
  513. height: 100%;
  514. position: relative;
  515. }
  516. .online-inquiry .bg-img {
  517. width: 100%;
  518. height: 100%;
  519. position: absolute;
  520. top: 0;
  521. left: 0;
  522. border-radius: 15rpx;
  523. }
  524. .index-cont {
  525. box-sizing: border-box;
  526. padding: 0 20upx 120rpx;
  527. }
  528. .official-account {
  529. width: 100%;
  530. }
  531. .popup-box {
  532. position: fixed;
  533. top: 0;
  534. right: 0;
  535. left: 0;
  536. bottom: 0;
  537. z-index: 999;
  538. display: flex;
  539. justify-content: center;
  540. align-items: center;
  541. }
  542. .popup-box .info-mask {
  543. position: fixed;
  544. top: 0;
  545. right: 0;
  546. bottom: 0;
  547. left: 0;
  548. background-color: rgba(0, 0, 0, 0.5);
  549. z-index: 999;
  550. }
  551. .popup-box .info-form {
  552. z-index: 1000;
  553. width: 450rpx;
  554. display: flex;
  555. flex-direction: column;
  556. align-items: center;
  557. }
  558. .popup-box .info-form image {
  559. width: 100%;
  560. }
  561. </style>