You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

659 lines
17 KiB

3 months ago
  1. <template>
  2. <!-- tab选项卡 -->
  3. <view class="index-product-wrapper" :style="[mbConfig]">
  4. <view class="nav-bd longTab" :style="[tabBgColor]">
  5. <scroll-view scroll-x="true" style="white-space: nowrap; display: flex" :scroll-left="tabLeft">
  6. <view class="longItem"
  7. :style="'color:' + (index == ProductNavindex ? checkColor : fontColor)+';--color:'+checkColor"
  8. :data-index="index" :class="index===ProductNavindex?'click':''" v-for="(item,index) in navList"
  9. :key="index" :id="'id'+index" @click="ProductNavTab(item, index)">{{ item.val }}
  10. </view>
  11. </scroll-view>
  12. </view>
  13. <view :style="[boxStyle]">
  14. <!-- 单列 -->
  15. <block v-if="itemStyle == 0">
  16. <view class="listA" :style="[gridGap]">
  17. <view class="item" v-for="(item, index) in tempArr" :key="index" @click="goDetail(item)">
  18. <view class="pictrue">
  19. <easy-loadimage :image-src="item.image"
  20. :radius="dataConfig.contentStyle.val"></easy-loadimage>
  21. <view v-if="item.activityStyle" :style="{ backgroundImage: `url(${item.activityStyle})` }" class="border-picture"></view>
  22. </view>
  23. <view class="text-info text-add">
  24. <view>
  25. <view class="title line2" :style="[titleColor]" v-if="showArr.includes(0)">
  26. <span>{{ item.storeName }}</span>
  27. </view>
  28. </view>
  29. <view v-if="item.productTags && item.productTags.locationUnderTitle.length">
  30. <text
  31. v-for="items in item.productTags.locationUnderTitle.length>3?item.productTags.locationUnderTitle.slice(0,3):item.productTags.locationUnderTitle"
  32. :key="items.id" class="mr10 tagSolid">{{items.tagName}}</text>
  33. </view>
  34. <view class="price acea-row row-middle" :style="[priceColor]">
  35. <view v-if="showArr.includes(1)">
  36. <span class="num semiBold">{{item.price}}</span>
  37. </view>
  38. </view>
  39. <view class="old-price" v-if="showArr.includes(2)" :style="[soldColor]">
  40. 已售 {{ item.sales || 0 }} {{ item.unitName }}
  41. </view>
  42. </view>
  43. </view>
  44. </view>
  45. </block>
  46. <!-- 两列 -->
  47. <block v-if="itemStyle == 1">
  48. <view class="listC" :style="[gridGap]">
  49. <view class="item" :style="[contentStyle]" v-for="(item, index) in tempArr" :key="index"
  50. @click="goDetail(item)">
  51. <view class="pictrue">
  52. <easy-loadimage :image-src="item.image"
  53. :radius="dataConfig.contentStyle.val"></easy-loadimage>
  54. <view v-if="item.activityStyle" :style="{ backgroundImage: `url(${item.activityStyle})` }" class="border-picture"></view>
  55. </view>
  56. <view class="text-info">
  57. <view class="title line2" :style="[titleColor]" v-if="showArr.includes(0)">
  58. <span>{{ item.storeName }}</span>
  59. </view>
  60. <view v-if="item.productTags && item.productTags.locationUnderTitle.length">
  61. <text
  62. v-for="items in item.productTags.locationUnderTitle.length>3?item.productTags.locationUnderTitle.slice(0,3):item.productTags.locationUnderTitle"
  63. :key="items.id" class="mr10 tagSolid">{{items.tagName}}</text>
  64. </view>
  65. <view class="row-middle price" :style="[priceColor]">
  66. <view v-if="showArr.includes(1)">
  67. <span class="num semiBold">{{item.price}}</span>
  68. </view>
  69. <view class="old-price ml10 " v-if="showArr.includes(2)" :style="[soldColor]">
  70. 已售 {{ item.sales || 0 }} {{ item.unitName }}
  71. </view>
  72. </view>
  73. </view>
  74. </view>
  75. </view>
  76. </block>
  77. <!-- 三列 -->
  78. <block v-if="itemStyle == 2">
  79. <view class="listB" :style="[gridGap]">
  80. <view class="item" v-for="(item, index) in tempArr" :key="index" @click="goDetail(item)">
  81. <view class="pictrue">
  82. <easy-loadimage :image-src="item.image"
  83. :radius="dataConfig.contentStyle.val"></easy-loadimage>
  84. <view v-if="item.activityStyle" :style="{ backgroundImage: `url(${item.activityStyle})` }" class="border-picture"></view>
  85. </view>
  86. <view class="text-info">
  87. <view class="title line2" :style="[titleColor]" v-if="showArr.includes(0)">
  88. <span>{{ item.storeName }}</span>
  89. </view>
  90. <view v-if="item.productTags && item.productTags.locationUnderTitle.length">
  91. <text
  92. v-for="items in item.productTags.locationUnderTitle.length>3?item.productTags.locationUnderTitle.slice(0,3):item.productTags.locationUnderTitle"
  93. :key="items.id" class="mr10 tagSolid">{{items.tagName}}</text>
  94. </view>
  95. <view class="price" :style="[priceColor]">
  96. <view v-if="showArr.includes(1)">
  97. <span class="num semiBold">{{item.price}}</span>
  98. </view>
  99. </view>
  100. <view class="old-price " v-if="showArr.includes(2)" :style="[soldColor]">
  101. 已售 {{ item.sales || 0 }} {{ item.unitName }}
  102. </view>
  103. </view>
  104. </view>
  105. </view>
  106. </block>
  107. <!-- 大图 -->
  108. <block v-if="itemStyle == 3 && tempArr.length">
  109. <view class="listBig" :style="[gridGap]">
  110. <view class="itemBig" v-for="(item,index) in tempArr" :key="index" @click="goDetail(item)">
  111. <view class="img-box">
  112. <easy-loadimage :image-src="item.image"
  113. :radius="dataConfig.contentStyle.val"></easy-loadimage>
  114. <view v-if="item.activityStyle" :style="{ backgroundImage: `url(${item.activityStyle})` }" class="border-picture"></view>
  115. </view>
  116. <view class="name line2" :style="[titleColor]" v-if="showArr.includes(0)">
  117. <span>{{item.storeName}}</span>
  118. </view>
  119. <view style="padding: 0 8px;"
  120. v-if="item.productTags && item.productTags.locationUnderTitle.length">
  121. <text
  122. v-for="items in item.productTags.locationUnderTitle.length>3?item.productTags.locationUnderTitle.slice(0,3):item.productTags.locationUnderTitle"
  123. :key="items.id" class="mr10 tagSolid">{{items.tagName}}</text>
  124. </view>
  125. <slot name="center"></slot>
  126. <view class="row-middle price" :style="[priceColor]">
  127. <span v-if="showArr.includes(1)">
  128. <span class="num semiBold">{{item.price}}</span>
  129. </span>
  130. <view class="old-price" v-if="showArr.includes(2)" :style="[soldColor]">
  131. 已售 {{ item.sales || 0 }} {{ item.unitName }}
  132. </view>
  133. </view>
  134. </view>
  135. </view>
  136. </block>
  137. </view>
  138. </view>
  139. </template>
  140. <script>
  141. // +----------------------------------------------------------------------
  142. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  143. // +----------------------------------------------------------------------
  144. // | Copyright (c) 2016~2025 https://www.crmeb.com All rights reserved.
  145. // +----------------------------------------------------------------------
  146. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  147. // +----------------------------------------------------------------------
  148. // | Author: CRMEB Team <admin@crmeb.com>
  149. // +----------------------------------------------------------------------
  150. import {
  151. getProductslist,productByidsApi
  152. } from '@/api/store.js';
  153. let app = getApp();
  154. import easyLoadimage from '@/components/base/easy-loadimage.vue';
  155. export default {
  156. name: 'homeTab',
  157. props: {
  158. dataConfig: {
  159. type: Object,
  160. default: () => {}
  161. }
  162. },
  163. data() {
  164. return {
  165. //普通价格
  166. svipPriceStyle: {
  167. svipBox: {
  168. height: '26rpx',
  169. borderRadius: '60rpx 56rpx 56rpx 20rpx',
  170. },
  171. icon: {
  172. height: '26rpx',
  173. fontSize: '18rpx',
  174. borderRadius: '12rpx 0 12rpx 2rpx'
  175. },
  176. price: {
  177. fontSize: '38rpx'
  178. },
  179. svipPrice: {
  180. fontSize: '22rpx'
  181. }
  182. },
  183. //svip价格
  184. svipIconStyle: {
  185. svipBox: {
  186. height: '26rpx',
  187. borderRadius: '24rpx 40rpx 40rpx 0.4rpx',
  188. },
  189. price: {
  190. fontSize: '38rpx'
  191. },
  192. svipPrice: {
  193. fontSize: '18rpx'
  194. }
  195. },
  196. tempArr: [],
  197. iSshowH: false,
  198. ProductNavindex: 0,
  199. itemStyle: 0, //样式类型
  200. themeColor: '#f00',
  201. titleConfig: 1, //标题位置
  202. infoColor: '#999',
  203. goodType: 3,
  204. loadend: false,
  205. loading: false,
  206. page: 1,
  207. isWidth: 0, //每个导航栏占位
  208. tabLeft: 0,
  209. limit: 0 ,//分页条数
  210. themeColor:this.$options.filters.filterTheme(app.globalData.theme)
  211. };
  212. },
  213. components: {
  214. easyLoadimage,
  215. },
  216. created() {
  217. let that = this
  218. uni.getSystemInfo({
  219. success(e) {
  220. that.isWidth = (e.windowWidth) / 5;
  221. }
  222. })
  223. },
  224. computed: {
  225. //标签文字颜色
  226. fontColor() {
  227. return this.dataConfig.fontColor.color[0].item
  228. },
  229. //选中颜色
  230. checkColor() {
  231. return this.dataConfig.checkThemeStyleConfig.tabVal?this.dataConfig.checkColor.color[0].item:this.themeColor
  232. },
  233. //选项卡背景颜色
  234. tabBgColor() {
  235. return {
  236. background: `linear-gradient(${this.dataConfig.tabBgColor.color[0].item}, ${this.dataConfig.tabBgColor.color[1].item})`,
  237. };
  238. },
  239. //页面间距
  240. mbConfig() {
  241. return {
  242. marginTop: this.dataConfig.mbConfig.val ? this.dataConfig.mbConfig.val + 'px' : 0
  243. }
  244. },
  245. //分类列表
  246. navList() {
  247. return this.dataConfig.tabItemConfig.list;
  248. },
  249. //最外层盒子的样式
  250. boxStyle() {
  251. return {
  252. borderRadius: this.dataConfig.bgStyle.val * 2 + 'rpx',
  253. background: `linear-gradient(${this.dataConfig.bgColor.color[0].item}, ${this.dataConfig.bgColor.color[1].item})`,
  254. margin: this.dataConfig.topConfig.val * 2 + 'rpx' + ' ' + this.dataConfig.lrConfig.val * 2 + 'rpx' +
  255. ' ' + 0,
  256. padding: this.dataConfig.upConfig.val * 2 + 'rpx' + ' ' + 0 + ' ' + this.dataConfig.downConfig.val *
  257. 2 + 'rpx'
  258. }
  259. },
  260. //商品间距
  261. gridGap() {
  262. return {
  263. 'grid-gap': this.dataConfig.contentConfig.val * 2 + 'rpx'
  264. }
  265. },
  266. //图片的圆角和高度
  267. imgStyle() {
  268. return {
  269. 'border-radius': this.dataConfig.contentStyle.val * 2 + 'rpx',
  270. }
  271. },
  272. //价格颜色
  273. priceColor() {
  274. return {
  275. 'color': this.dataConfig.priceThemeStyleConfig.tabVal?this.dataConfig.priceColor.color[0].item:this.themeColor,
  276. }
  277. },
  278. //商品名称颜色
  279. titleColor() {
  280. return {
  281. 'color': this.dataConfig.titleColor.color[0].item,
  282. }
  283. },
  284. //已售数量
  285. soldColor() {
  286. return {
  287. 'color': this.dataConfig.soldColor.color[0].item,
  288. }
  289. },
  290. showArr(){
  291. return this.dataConfig.tabItemConfig.list[this.ProductNavindex].activeList.showContent
  292. },
  293. //商品名称
  294. titleShow() {
  295. if (this.dataConfig.typeConfig.activeValue.includes(0)) {
  296. return true;
  297. } else {
  298. return false;
  299. }
  300. },
  301. //价格
  302. priceShow() {
  303. if (this.dataConfig.typeConfig.activeValue.includes(1)) {
  304. return true;
  305. } else {
  306. return false;
  307. }
  308. },
  309. //销量
  310. soldShow() {
  311. if (this.dataConfig.typeConfig.activeValue.includes(2)) {
  312. return true;
  313. } else {
  314. return false;
  315. }
  316. },
  317. //内容圆角
  318. contentStyle() {
  319. return {
  320. 'border-radius': this.dataConfig.contentStyle.val ? this.dataConfig.contentStyle.val + 'px' : '0'
  321. };
  322. },
  323. },
  324. mounted() {
  325. //默认加载第一项的商品数据
  326. if (this.navList) {
  327. this.itemStyle = this.navList[0].activeList ? this.navList[0].activeList.styleType : 0;
  328. if (this.navList[0].activeList && this.navList[0].activeList.activeProTabIndex == 0) {
  329. this.getProductByids(this.navList[0].activeList.goods);
  330. } else {
  331. this.limit = this.navList[0].activeList ? this.navList[0].activeList.num : 3;
  332. if (this.navList[0].activeList) {
  333. this.getGroomList(this.navList[0].activeList);
  334. }
  335. }
  336. }
  337. },
  338. //uniapp小程序用deep重写组件样式不生效
  339. options: {
  340. styleIsolation: 'shared'
  341. },
  342. methods: {
  343. //根据商品id集合查询对应商品
  344. getProductByids(data) {
  345. if(!data.length) return;
  346. uni.showLoading({
  347. title: '加载中...'
  348. });
  349. let ids = data.map((item) => item.id).join(',');
  350. productByidsApi(ids).then((res) => {
  351. this.tempArr = res.data;
  352. uni.hideLoading();
  353. })
  354. .catch(res => {
  355. uni.hideLoading();
  356. });
  357. },
  358. // 选项卡切换点击事件;商品类型选择除第一个指定商品,加载商品从平台端获取数据,其余选项均请求接口加载
  359. changeTab(item, index) {
  360. this.tempArr = [];
  361. if (item.activeList.activeProTabIndex == 0) {
  362. this.getProductByids(item.activeList.goods);
  363. } else {
  364. this.page = 1;
  365. this.loadend = false;
  366. this.getGroomList(item.activeList);
  367. }
  368. },
  369. // 商品列表
  370. getGroomList(item) {
  371. let cid = item.activeValue; //分类id
  372. let goodsSort = item.goodsSort // 商品排序,0综合,1按销量,2按价格
  373. let priceOrder = '';
  374. let salesOrder = '';
  375. if (this.loadend) return false;
  376. if (this.loading) return false;
  377. if (goodsSort === 0) {
  378. priceOrder = '';
  379. salesOrder = '';
  380. } else if (goodsSort === 1) {
  381. priceOrder = '';
  382. salesOrder = 'desc';
  383. } else {
  384. priceOrder = 'desc';
  385. salesOrder = '';
  386. }
  387. getProductslist({
  388. page: this.page,
  389. limit: this.limit,
  390. cid: cid,
  391. priceOrder: priceOrder,
  392. salesOrder: salesOrder
  393. }).then((res) => {
  394. let list = res.data.list;
  395. this.tempArr = this.$util.SplitArray(list, this.tempArr);
  396. let loadend = list.length < this.limit;
  397. this.loadend = loadend;
  398. this.loading = false;
  399. this.page = this.page + 1;
  400. })
  401. .catch(res => {
  402. this.loading = false;
  403. });
  404. },
  405. // 选项卡切换
  406. ProductNavTab(item, index) {
  407. this.ProductNavindex = index;
  408. this.itemStyle = this.navList[index].activeList.styleType;
  409. this.$nextTick(() => {
  410. let id = 'id' + index;
  411. this.tabLeft = (index - 2) * this.isWidth //设置下划线位置
  412. })
  413. this.limit = item.activeList.num;
  414. this.changeTab(item, index);
  415. },
  416. goDetail(item) {
  417. uni.navigateTo({
  418. url: `/pages/goods/goods_details/index?id=${item.id}`
  419. })
  420. }
  421. }
  422. }
  423. </script>
  424. <style lang="scss" scoped>
  425. .longTab {
  426. .longItem {
  427. height: 70rpx;
  428. display: inline-block;
  429. line-height: 70rpx;
  430. text-align: center;
  431. font-size: 28rpx;
  432. color: #333333;
  433. white-space: nowrap;
  434. overflow: hidden;
  435. text-overflow: ellipsis;
  436. margin-right: 46rpx;
  437. &.click {
  438. font-weight: bold;
  439. font-size: 30rpx;
  440. position: relative;
  441. &::after {
  442. content: '';
  443. width: 40rpx;
  444. height: 4rpx;
  445. background: var(--color);
  446. position: absolute;
  447. bottom: 0;
  448. left: 50%;
  449. transform: translateX(-50%);
  450. }
  451. }
  452. }
  453. }
  454. .index-product-wrapper {
  455. &.on {
  456. min-height: 1500rpx;
  457. }
  458. .nav-bd {
  459. height: 70rpx;
  460. line-height: 70rpx;
  461. padding-left: 20rpx;
  462. background: #fff;
  463. .item {
  464. display: inline-block;
  465. font-size: 28rpx;
  466. color: #333;
  467. font-weight: 400;
  468. padding-right: 48rpx;
  469. &.on {
  470. border-radius: 0;
  471. }
  472. }
  473. }
  474. }
  475. .text-add {
  476. display: flex;
  477. flex-direction: column;
  478. justify-content: space-between;
  479. }
  480. .listBig {
  481. display: grid;
  482. grid-template-rows: auto;
  483. grid-template-columns: repeat(1, 1fr);
  484. padding: 0 20rpx;
  485. .itemBig {
  486. width: 100%;
  487. .img-box {
  488. width: 100%;
  489. height: 710rpx;
  490. position: relative;
  491. }
  492. .name {
  493. font-size: 28rpx;
  494. font-weight: bold;
  495. margin-top: 16rpx;
  496. // padding: 0 8px;
  497. }
  498. .price {
  499. font-weight: bold;
  500. font-size: 12px;
  501. margin-top: 10rpx;
  502. // padding: 0 8px;
  503. .num {
  504. font-size: 32rpx;
  505. margin-right: 10rpx;
  506. }
  507. .old-price {
  508. color: #aaa;
  509. font-weight: normal;
  510. }
  511. }
  512. }
  513. }
  514. .listA {
  515. display: grid;
  516. grid-template-columns: repeat(1, 1fr);
  517. grid-template-rows: auto;
  518. width: 100%;
  519. padding: 0 20rpx;
  520. .item {
  521. display: flex;
  522. width: 100%;
  523. .pictrue {
  524. width: 220rpx;
  525. height: 220rpx;
  526. position: relative;
  527. image {
  528. width: 100%;
  529. height: 100%;
  530. }
  531. }
  532. .text-info {
  533. margin-left: 14rpx;
  534. flex: 1
  535. }
  536. }
  537. }
  538. .listB {
  539. display: grid;
  540. grid-template-columns: repeat(3, 1fr);
  541. grid-template-rows: auto;
  542. width: 100%;
  543. padding: 0 20rpx;
  544. .item {
  545. .pictrue {
  546. width: 100%;
  547. height: 220rpx;
  548. position: relative;
  549. image {
  550. width: 100%;
  551. height: 100%;
  552. }
  553. }
  554. .text-info {
  555. padding: 10rpx 0;
  556. }
  557. }
  558. }
  559. .listC {
  560. display: grid;
  561. grid-template-columns: repeat(2, 1fr);
  562. grid-template-rows: auto;
  563. width: 100%;
  564. padding: 0 20rpx;
  565. /deep/.origin-img,
  566. /deep/.easy-loadimage {
  567. border-bottom-left-radius: 0 !important;
  568. border-bottom-right-radius: 0 !important;
  569. }
  570. .item {
  571. background-color: #fff;
  572. overflow: hidden;
  573. .pictrue {
  574. width: 100%;
  575. height: 345rpx;
  576. overflow: hidden;
  577. position: relative;
  578. image {
  579. width: 100%;
  580. height: 100%;
  581. }
  582. }
  583. .text-info {
  584. padding: 16rpx 0 16rpx 16rpx;
  585. .title {
  586. width: 300rpx;
  587. }
  588. }
  589. }
  590. }
  591. .text-info {
  592. .title {
  593. width: 100%;
  594. height: 80rpx;
  595. line-height: 40rpx;
  596. color: #333;
  597. }
  598. .old-price {
  599. font-weight: normal;
  600. font-size: 24rpx;
  601. color: #999;
  602. }
  603. .price {
  604. font-size: 36rpx;
  605. font-weight: 550;
  606. text {
  607. padding-bottom: 4rpx;
  608. font-size: 26rpx;
  609. font-weight: normal;
  610. }
  611. }
  612. }
  613. .mer_badge {
  614. padding: 0 4rpx;
  615. background-color: theme;
  616. color: #fff;
  617. font-size: 20rpx;
  618. display: inline-block;
  619. border-radius: 4rpx;
  620. line-height: 28rpx;
  621. height: 28rpx;
  622. }
  623. </style>