productBottom.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <template>
  2. <commonWrapper
  3. :config="wrapperConfig"
  4. class="footer"
  5. :class="{ eject: storeInfo.id }"
  6. :style="bagStyle"
  7. >
  8. <view class="acea-row row-between-wrapper px-20 py-14" style="height: 100%">
  9. <div class="acea-row">
  10. <block v-if="!isCustomEntry">
  11. <block v-for="(item_id, index) in showIcons" :key="index">
  12. <navigator
  13. v-if="item_id === 3"
  14. hover-class="none"
  15. class="item"
  16. open-type="reLaunch"
  17. url="/pages/index/index"
  18. >
  19. <view class="iconfont icon-shouye6"></view>
  20. <view class="p_center">{{ $t(`首页`) }}</view>
  21. </navigator>
  22. <view v-if="item_id === 1" @click="setCollect" class="item">
  23. <view
  24. class="iconfont icon-shoucang1"
  25. v-if="storeInfo.userCollect"
  26. ></view>
  27. <view class="iconfont icon-shoucang4" v-else></view>
  28. <view class="p_center">{{ $t(`收藏`) }}</view>
  29. </view>
  30. <view
  31. v-if="item_id === 2"
  32. class="animated item"
  33. :class="animated == true ? 'bounceIn' : ''"
  34. @click="goCart"
  35. >
  36. <view class="iconfont icon-gouwuche">
  37. <text class="num bg-color" v-if="parseFloat(CartCount) > 0">{{
  38. CartCount || 0
  39. }}</text>
  40. </view>
  41. <view class="p_center">{{ $t(`购物车`) }}</view>
  42. </view>
  43. <!-- #ifdef APP-PLUS || H5 -->
  44. <view
  45. v-if="item_id === 0"
  46. class="item"
  47. @click="goCustomer"
  48. >
  49. <view class="iconfont icon-kefu"></view>
  50. <view class="p_center">{{ $t(`客服`) }}</view>
  51. </view>
  52. <!-- #endif -->
  53. <!-- #ifdef MP -->
  54. <view
  55. v-if="item_id === 0 && routineContact == 0"
  56. class="item"
  57. @click="goCustomer"
  58. >
  59. <view class="iconfont icon-kefu"></view>
  60. <view class="p_center">{{ $t(`客服`) }}</view>
  61. </view>
  62. <button
  63. v-else-if="item_id === 0 && routineContact == 1"
  64. class="item"
  65. open-type="contact"
  66. :send-message-title="storeInfo.store_name"
  67. :send-message-img="storeInfo.image"
  68. :send-message-path="`/pages/goods_details/index?id=${storeInfo.id}`"
  69. show-message-card
  70. hover-class="none"
  71. >
  72. <view class="iconfont icon-kefu"></view>
  73. <view class="p_center">{{ $t(`客服`) }}</view>
  74. </button>
  75. <!-- #endif -->
  76. <view v-if="item_id === 4" class="item" @click="goShare">
  77. <view class="iconfont icon-fenxiang4"></view>
  78. <view class="p_center">{{ $t(`分享`) }}</view>
  79. </view>
  80. </block>
  81. <view v-if="is_gift" @click="goGift()" class="item">
  82. <image
  83. class="gift-icon"
  84. src="@/static/images/gift-icon.png"
  85. mode=""
  86. ></image>
  87. <view class="p_center">{{ $t(`送礼物`) }}</view>
  88. </view>
  89. </block>
  90. <block v-else>
  91. <view
  92. class="item"
  93. v-for="(item, index) in customMenuList"
  94. :key="index"
  95. @click="goPage(item.url)"
  96. >
  97. <view
  98. v-if="isCustomIcon"
  99. class="iconfont"
  100. :class="item.icon"
  101. :style="customIconStyle"
  102. ></view>
  103. <image
  104. v-else
  105. :src="item.img"
  106. class="menu-img"
  107. :style="customImageStyle"
  108. mode="aspectFit"
  109. ></image>
  110. <view class="p_center">{{ item.name }}</view>
  111. </view>
  112. <view v-if="is_gift" @click="goGift()" class="item">
  113. <image
  114. class="gift-icon"
  115. src="@/static/images/gift-icon.png"
  116. mode=""
  117. ></image>
  118. <view class="p_center">{{ $t(`送礼物`) }}</view>
  119. </view>
  120. </block>
  121. </div>
  122. <view v-if="noGoods" class="presale">
  123. <view class="acea-row">
  124. <form class="bnts bg-color-hui">
  125. <button class="bnts bg-color-hui" form-type="submit">
  126. {{ $t(`暂无产品`) }}
  127. </button>
  128. </form>
  129. </view>
  130. </view>
  131. <view class="btn-box" v-else>
  132. <view v-if="!storeInfo.presale">
  133. <view
  134. class="bnt acea-row"
  135. :class="!isCartButtonVisible ? 'virbnt' : ''"
  136. v-if="attr.productSelect.stock <= 0"
  137. >
  138. <form
  139. v-if="isCartButtonVisible"
  140. @submit="joinCart"
  141. class="joinCart bnts"
  142. :class="!isCartButtonVisible ? 'virbnt' : ''"
  143. >
  144. <button
  145. class="joinCart bnts"
  146. form-type="submit"
  147. :style="cartBtnStyle"
  148. >
  149. {{ $t(`加入购物车`) }}
  150. </button>
  151. </form>
  152. <form class="buy bnts bg-color-hui">
  153. <button
  154. class="buy bnts bg-color-hui"
  155. form-type="submit"
  156. :class="!isCartButtonVisible ? 'virbnt' : ''"
  157. >
  158. {{ $t(`已售罄`) }}
  159. </button>
  160. </form>
  161. </view>
  162. <view class="bnt acea-row" v-else>
  163. <form
  164. v-if="isCartButtonVisible"
  165. @submit="joinCart"
  166. class="joinCart bnts"
  167. >
  168. <button
  169. class="joinCart bnts"
  170. form-type="submit"
  171. :style="cartBtnStyle"
  172. >
  173. {{ $t(`加入购物车`) }}
  174. </button>
  175. </form>
  176. <form
  177. @submit="goBuy"
  178. class="buy bnts"
  179. :class="!isCartButtonVisible ? 'virbnt' : ''"
  180. >
  181. <button
  182. class="buy bnts"
  183. :class="!isCartButtonVisible ? 'virbnt' : ''"
  184. form-type="submit"
  185. :style="buyBtnStyle"
  186. >
  187. {{ $t(`立即购买`) }}
  188. </button>
  189. </form>
  190. </view>
  191. </view>
  192. <view class="presale" v-else>
  193. <view
  194. class="acea-row"
  195. v-if="presale_pay_status === 1 || presale_pay_status === 3"
  196. >
  197. <form class="bnts bg-color-hui">
  198. <button class="bnts bg-color-hui" form-type="submit">
  199. {{ presale_pay_status === 1 ? $t(`未开始`) : $t(`已结束`) }}
  200. </button>
  201. </form>
  202. </view>
  203. <view
  204. class="acea-row"
  205. v-else-if="
  206. attr.productSelect.quota <= 0 ||
  207. attr.productSelect.quota < attr.productSelect.cart_num
  208. "
  209. >
  210. <form class="bnts bg-color-hui">
  211. <button class="bnts bg-color-hui" form-type="submit">
  212. {{ $t(`已售罄`) }}
  213. </button>
  214. </form>
  215. </view>
  216. <view class="bnts acea-row" v-else-if="presale_pay_status === 2">
  217. <form @submit="goBuy" class="bnts">
  218. <button class="bnts" form-type="submit" :style="buyBtnStyle">
  219. {{ $t(`立即购买`) }}
  220. </button>
  221. </form>
  222. </view>
  223. </view>
  224. </view>
  225. </view>
  226. </commonWrapper>
  227. </template>
  228. <script>
  229. import commonWrapper from "./commonWrapper.vue";
  230. import { getCustomer } from "@/utils/index.js";
  231. export default {
  232. name: "productBottom",
  233. components: {
  234. commonWrapper,
  235. },
  236. props: {
  237. diyData: {
  238. type: Object,
  239. default: () => ({}),
  240. },
  241. storeInfo: {
  242. type: Object,
  243. default: () => ({}),
  244. },
  245. is_gift: {
  246. type: [Boolean, Number],
  247. default: 0,
  248. },
  249. CartCount: {
  250. type: [Number, String],
  251. default: 0,
  252. },
  253. noGoods: {
  254. type: Boolean,
  255. default: false,
  256. },
  257. attr: {
  258. type: Object,
  259. default: () => ({
  260. productSelect: {},
  261. }),
  262. },
  263. presale_pay_status: {
  264. type: [Number, String],
  265. default: 1,
  266. },
  267. animated: {
  268. type: Boolean,
  269. default: false,
  270. },
  271. routineContact: {
  272. type: Number,
  273. default: 0,
  274. },
  275. },
  276. computed: {
  277. bottomConfig() {
  278. if (!this.diyData || !this.diyData.value) return null;
  279. const values = Object.values(this.diyData.value);
  280. return values.find((item) => item.name === "bottomMenu");
  281. },
  282. wrapperConfig() {
  283. let config = this.bottomConfig;
  284. if (!config) return null;
  285. let newConfig = { ...config };
  286. return newConfig;
  287. },
  288. toneConfig() {
  289. if (!this.bottomConfig || !this.bottomConfig.toneConfig) return 0;
  290. return this.bottomConfig.toneConfig.tabVal;
  291. },
  292. bagStyle() {
  293. if (this.bottomConfig) {
  294. // const color = this.bottomConfig.bottomBgColor.color[0].item || this.bottomConfig.bottomBgColor.default[0].item;
  295. // return `background-color: ${color}`;
  296. const color = this.bottomConfig.componentBgConfig.colorConfig.color;
  297. const c1 = color[0].item;
  298. const c2 = color[1].item;
  299. return `background: linear-gradient(90deg, ${c1} 0%, ${c2} 100%);`;
  300. }
  301. },
  302. cartBtnStyle() {
  303. if (this.toneConfig && this.bottomConfig.cartColor) {
  304. const color = this.bottomConfig.cartColor.color;
  305. const c1 = color[0].item;
  306. const c2 = color[1].item;
  307. return `background: linear-gradient(90deg, ${c1} 0%, ${c2} 100%);`;
  308. }
  309. return ""; // Fallback to CSS default
  310. },
  311. buyBtnStyle() {
  312. if (this.toneConfig && this.bottomConfig.buyColor) {
  313. const color = this.bottomConfig.buyColor.color;
  314. const c1 = color[0].item;
  315. const c2 = color[1].item;
  316. return `background: linear-gradient(90deg, ${c1} 0%, ${c2} 100%);`;
  317. }
  318. return ""; // Fallback to CSS default
  319. },
  320. showIcons() {
  321. if (!this.bottomConfig) return [3, 1, 2, 0, 4, 5];
  322. return this.bottomConfig.showContent.type;
  323. },
  324. showCartButton() {
  325. if (!this.bottomConfig) return true;
  326. return this.bottomConfig.cartButton.tabVal === 0;
  327. },
  328. isCartButtonVisible() {
  329. return !!this.storeInfo.cart_button && this.showCartButton;
  330. },
  331. entryConfig() {
  332. return this.bottomConfig && this.bottomConfig.entryConfig;
  333. },
  334. menuConfig() {
  335. return this.bottomConfig && this.bottomConfig.menuConfig;
  336. },
  337. isCustomEntry() {
  338. return this.entryConfig && this.entryConfig.tabVal === 1;
  339. },
  340. isCustomImage() {
  341. return this.isCustomEntry && this.menuConfig.listStyle === 0;
  342. },
  343. isCustomIcon() {
  344. return this.isCustomEntry && this.menuConfig.listStyle === 1;
  345. },
  346. customMenuList() {
  347. if (!this.isCustomEntry || !this.menuConfig) return [];
  348. return this.menuConfig.list
  349. .filter((item) => item.show)
  350. .map((item) => ({
  351. name: item.info[0].value,
  352. url: item.info[1].value,
  353. icon: item.icon,
  354. img: item.img,
  355. }));
  356. },
  357. customImageStyle() {
  358. const fillet = this.bottomConfig?.fillet;
  359. if (!fillet) return { width: "40rpx", height: "40rpx" };
  360. let radius;
  361. if (fillet.type) {
  362. radius = `${fillet.valList[0].val}px ${fillet.valList[1].val}px ${fillet.valList[3].val}px ${fillet.valList[2].val}px`;
  363. } else {
  364. radius = `${fillet.val}px`;
  365. }
  366. return {
  367. borderRadius: radius,
  368. width: "40rpx",
  369. height: "40rpx",
  370. display: "block",
  371. };
  372. },
  373. customIconStyle() {
  374. const config = this.bottomConfig;
  375. if (!config) return {};
  376. const color = config.iconColor?.color?.[0]?.item || "#333";
  377. const size = config.iconSize?.val || 20;
  378. const rotate = config.iconRotate?.val || 0;
  379. const padding = config.padding?.val || 0;
  380. const shadow =
  381. config.shadow?.tabVal === 1 ? "0px 2px 4px rgba(0,0,0,0.2)" : "none";
  382. return {
  383. color: color,
  384. fontSize: `${size}px`,
  385. transform: `rotate(${rotate}deg)`,
  386. padding: `${padding}px`,
  387. textShadow: shadow,
  388. display: "inline-block",
  389. };
  390. },
  391. },
  392. methods: {
  393. goCustomer() {
  394. getCustomer(`/pages/extension/customer_list/chat?productId=${this.storeInfo.id}`);
  395. },
  396. goPage(url) {
  397. if (!url) return;
  398. uni.navigateTo({
  399. url: url,
  400. fail: () => {
  401. uni.switchTab({
  402. url: url,
  403. });
  404. },
  405. });
  406. },
  407. setCollect() {
  408. this.$emit("setCollect");
  409. },
  410. goCart() {
  411. this.$emit("goCart");
  412. },
  413. goGift() {
  414. this.$emit("goGift");
  415. },
  416. joinCart() {
  417. this.$emit("joinCart");
  418. },
  419. goBuy() {
  420. this.$emit("goBuy");
  421. },
  422. },
  423. };
  424. </script>
  425. <style scoped lang="scss">
  426. .footer {
  427. position: fixed;
  428. bottom: 0;
  429. width: 100%;
  430. box-sizing: border-box;
  431. z-index: 277;
  432. border-top: 1rpx solid #f0f0f0; // 保留默认边框作为兜底
  433. height: 100rpx;
  434. height: calc(100rpx + constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
  435. height: calc(100rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
  436. transform: translate3d(0, 100%, 0);
  437. transition: all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);
  438. &.eject {
  439. transform: translate3d(0, 0, 0);
  440. }
  441. .gift-icon {
  442. width: 40rpx;
  443. height: 40rpx;
  444. margin: 5rpx 0 0rpx;
  445. }
  446. .item {
  447. display: flex;
  448. flex-direction: column;
  449. justify-content: center;
  450. align-items: center;
  451. font-size: 18rpx;
  452. margin-right: 30rpx;
  453. color: #666;
  454. .iconfont {
  455. text-align: center;
  456. font-size: 34rpx;
  457. &.icon-shoucang1 {
  458. color: var(--view-theme);
  459. }
  460. &.icon-gouwuche {
  461. position: relative;
  462. .num {
  463. color: #fff;
  464. position: absolute;
  465. font-size: 18rpx;
  466. padding: 2rpx 11rpx 3rpx;
  467. border-radius: 200rpx;
  468. top: -10rpx;
  469. right: -14rpx;
  470. }
  471. }
  472. }
  473. }
  474. uni-button {
  475. background-color: transparent;
  476. }
  477. .bnt {
  478. flex: 1;
  479. height: 76rpx;
  480. display: flex;
  481. justify-content: center;
  482. align-items: center;
  483. flex-wrap: nowrap;
  484. .bnts {
  485. width: 100%;
  486. text-align: center;
  487. line-height: 76rpx;
  488. color: #fff;
  489. font-size: 28rpx;
  490. border-radius: 50rpx;
  491. }
  492. .joinCart {
  493. background-color: var(--view-bntColor);
  494. margin-right: 20rpx;
  495. }
  496. .buy {
  497. background-color: var(--view-theme);
  498. }
  499. }
  500. }
  501. .virbnt {
  502. // width: 444rpx !important;
  503. height: 76rpx !important;
  504. border-radius: 50rpx !important;
  505. overflow: hidden;
  506. }
  507. .virbnts {
  508. width: 444rpx !important;
  509. text-align: center;
  510. line-height: 76rpx;
  511. color: #fff;
  512. font-size: 28rpx;
  513. background-color: var(--view-bntColor);
  514. border-radius: 50rpx !important;
  515. }
  516. .btn-box {
  517. flex: 1;
  518. }
  519. .presale .bnts {
  520. width: 100%;
  521. height: 76rpx;
  522. border-radius: 50rpx 50rpx;
  523. background-color: var(--view-theme);
  524. text-align: center;
  525. line-height: 76rpx;
  526. color: #fff;
  527. font-size: 28rpx;
  528. }
  529. </style>