pageDesign.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. <template>
  2. <view class="page-design" :class="bgClass">
  3. <view v-if="!errorNetwork" :style="colorStyle">
  4. <!-- #ifdef MP -->
  5. <view
  6. class="fixed z-1000"
  7. :style="[appletStyle]"
  8. v-if="myApplet && isHome"
  9. >
  10. <view
  11. class="myApplet w-324 h-62 text-center rd-12rpx lh-62rpx fs-24 bg--w111-fff text-w111-303133"
  12. >
  13. 点击添加到我的小程序
  14. <text
  15. class="iconfont icon-ic_close2 text--w111-ccc ml-16"
  16. @click="myApplet = false"
  17. ></text>
  18. </view>
  19. </view>
  20. <!-- #endif -->
  21. <!-- 轮播搜索 -->
  22. <homeComb
  23. v-if="showHomeComb"
  24. :dataConfig="homeCombData"
  25. :belongIndex="belongIndex"
  26. @bindSortId="bindSortId"
  27. :isScrolled="isScrolled"
  28. @storeTap="storeTap"
  29. ></homeComb>
  30. <!-- 顶部搜索框 -->
  31. <headerSerch
  32. v-if="isHeaderSerch"
  33. :dataConfig="headerSerchCombData"
  34. :belongIndex="belongIndex"
  35. @storeTap="storeTap"
  36. ></headerSerch>
  37. <tabNav
  38. v-if="showCateNav"
  39. :dataConfig="cateNavData"
  40. @bindHeight="bindHeight"
  41. @bindSortId="bindSortId"
  42. :isFixed="isFixed && !cateNavData.stickyConfig.tabVal"
  43. ></tabNav>
  44. <view class="index">
  45. <!-- 自定义样式 -->
  46. <block v-for="(item, index) in styleConfig" :key="index">
  47. <view :id="item.id">
  48. <userInfor
  49. v-if="item.name == 'userInfor'"
  50. :dataConfig="item"
  51. @changeLogin="changeLogin"
  52. ></userInfor>
  53. <homeUserInfor
  54. v-else-if="item.name == 'member'"
  55. :dataConfig="item"
  56. @changeLogin="changeLogin"
  57. ></homeUserInfor>
  58. <newVip
  59. v-else-if="item.name == 'newVip'"
  60. :dataConfig="item"
  61. ></newVip>
  62. <!-- 文章列表 -->
  63. <articleList
  64. v-else-if="item.name == 'articleList'"
  65. :dataConfig="item"
  66. ></articleList>
  67. <bargain
  68. v-else-if="item.name == 'bargain'"
  69. :dataConfig="item"
  70. @changeBarg="changeBarg"
  71. ></bargain>
  72. <blankPage
  73. v-else-if="item.name == 'blankPage'"
  74. :dataConfig="item"
  75. ></blankPage>
  76. <combination
  77. v-else-if="item.name == 'combination'"
  78. :dataConfig="item"
  79. ></combination>
  80. <!-- 优惠券 -->
  81. <coupon
  82. v-else-if="item.name == 'coupon'"
  83. :dataConfig="item"
  84. @changeLogin="changeLogin"
  85. ></coupon>
  86. <!-- 客户服务 -->
  87. <customerService
  88. v-else-if="item.name == 'customerService'"
  89. :dataConfig="item"
  90. ></customerService>
  91. <!-- 商品列表 -->
  92. <goodList
  93. ref="goodLists"
  94. v-else-if="
  95. item.name == 'goodList' || item.name == 'goodRecommend'
  96. "
  97. :dataConfig="item"
  98. :list="goodList"
  99. ></goodList>
  100. <!-- <homeGoodRecommend
  101. v-else-if="
  102. item.name == 'goodList' || item.name == 'goodRecommend'
  103. "
  104. :dataConfig="item"
  105. ></homeGoodRecommend> -->
  106. <guide v-else-if="item.name == 'guide'" :dataConfig="item"></guide>
  107. <!-- 直播模块 -->
  108. <!-- #ifdef MP-WEIXIN -->
  109. <liveBroadcast
  110. v-else-if="item.name == 'liveBroadcast'"
  111. :dataConfig="item"
  112. ></liveBroadcast>
  113. <!-- #endif -->
  114. <menus v-else-if="item.name == 'menus'" :dataConfig="item"></menus>
  115. <!-- 实时消息 -->
  116. <news v-else-if="item.name == 'news'" :dataConfig="item"></news>
  117. <!-- 图片库 -->
  118. <pictureCube
  119. v-else-if="item.name == 'pictureCube'"
  120. :dataConfig="item"
  121. ></pictureCube>
  122. <!-- 促销列表 -->
  123. <promotionList
  124. ref="promotionLists"
  125. v-else-if="item.name == 'promotionList'"
  126. :dataConfig="item"
  127. :productVideoStatus="productVideoStatus"
  128. :positionTop="positionTop"
  129. ></promotionList>
  130. <seckill
  131. v-else-if="item.name == 'seckill'"
  132. :dataConfig="item"
  133. ></seckill>
  134. <!-- 轮播图-->
  135. <swiperBg
  136. v-else-if="item.name == 'swiperBg'"
  137. :dataConfig="item"
  138. ></swiperBg>
  139. <swipers
  140. v-else-if="item.name == 'swipers'"
  141. :dataConfig="item"
  142. ></swipers>
  143. <!-- 标题 -->
  144. <titles
  145. v-else-if="item.name == 'titles'"
  146. :dataConfig="item"
  147. ></titles>
  148. <presale
  149. v-else-if="item.name == 'presale'"
  150. :dataConfig="item"
  151. ></presale>
  152. <pointsMall
  153. v-else-if="item.name == 'pointsMall'"
  154. :dataConfig="item"
  155. ></pointsMall>
  156. <!-- #ifndef APP -->
  157. <richText
  158. v-else-if="item.name == 'richText'"
  159. :dataConfig="item"
  160. ></richText>
  161. <videos
  162. v-else-if="item.name == 'videos'"
  163. :dataConfig="item"
  164. ></videos>
  165. <!-- #endif -->
  166. <signIn
  167. v-else-if="item.name == 'signIn'"
  168. :dataConfig="item"
  169. ></signIn>
  170. <hotspot
  171. v-else-if="item.name == 'hotspot'"
  172. :dataConfig="item"
  173. ></hotspot>
  174. <follow
  175. v-else-if="item.name == 'follow'"
  176. :dataConfig="item"
  177. ></follow>
  178. <!-- 商品详情 -->
  179. <productInfo
  180. v-else-if="item.name == 'productInfo'"
  181. :dataConfig="item"
  182. :productData="productData"
  183. :priceData="priceData"
  184. :skuList="skuList"
  185. @changeSpec="onChangeSpec"
  186. @showSpecModal="onShowSpecModal"
  187. @share="onShare"
  188. @goActivity="onGoActivity"
  189. ></productInfo>
  190. <homePaidVip
  191. v-else-if="item.name == 'home_paid_vip'"
  192. :dataConfig="item"
  193. :productData="productData"
  194. :isShowPaidVip="isShowPaidVip"
  195. :priceData="priceData"
  196. ></homePaidVip>
  197. <homeProductService
  198. v-else-if="item.name == 'productService'"
  199. :dataConfig="item"
  200. :productData="productData"
  201. :couponList="couponList"
  202. :activity="activity"
  203. :attr="attr"
  204. :attrTxt="attrTxt"
  205. :attrValue="attrValue"
  206. @showCoupon="onShowCoupon"
  207. @showSpecModal="onShowSpecModal"
  208. @openModal="onOpenModal"
  209. @goActivity="onGoActivity"
  210. ></homeProductService>
  211. <homeReviews
  212. v-else-if="item.name == 'reviews'"
  213. :dataConfig="item"
  214. :reply="reply"
  215. :replyCount="replyCount"
  216. :replyChance="replyChance"
  217. :productId="productId"
  218. ></homeReviews>
  219. <productDesc
  220. v-else-if="item.name == 'productDesc'"
  221. :dataConfig="item"
  222. :productData="productData"
  223. ></productDesc>
  224. <customComponent
  225. v-else-if="item.name == 'customComponent'"
  226. :dataConfig="item"
  227. @changeLogin="changeLogin"
  228. ></customComponent>
  229. </view>
  230. </block>
  231. <!-- 插槽:用于展示分类商品列表或其他底部内容 -->
  232. <slot name="bottom"></slot>
  233. <view class="pb-safe" :style="[pdHeights]" v-if="isFooter"></view>
  234. <pageFooter
  235. v-if="footerConfigData"
  236. :configData="footerConfigData"
  237. @newDataStatus="newDataStatus"
  238. ></pageFooter>
  239. </view>
  240. </view>
  241. <view v-else>
  242. <view class="error-network">
  243. <image :src="imgHost + '/statics/images/error-network.gif'"></image>
  244. <view class="title">{{ $t(`网络连接断开`) }}</view>
  245. <view class="btn" @click="reconnect">{{ $t(`重新连接`) }}</view>
  246. </view>
  247. </view>
  248. </view>
  249. </template>
  250. <script>
  251. import pageFooter from "@/components/pageFooter/index.vue";
  252. import { HTTP_REQUEST_URL } from "@/config/app";
  253. import colors from "@/mixins/color";
  254. // diyComponents - 同目录使用相对路径
  255. import homeComb from "./homeComb.vue";
  256. import headerSerch from "./headerSerch.vue";
  257. import tabNav from "./tabNav.vue";
  258. import userInfor from "./userInfor.vue";
  259. import homeUserInfor from "./homeUserInfor.vue";
  260. import newVip from "./newVip.vue";
  261. import articleList from "./articleList.vue";
  262. import bargain from "./bargain.vue";
  263. import blankPage from "./blankPage.vue";
  264. import combination from "./combination.vue";
  265. import coupon from "./coupon.vue";
  266. import customerService from "./customerService.vue";
  267. import goodList from "./goodList.vue";
  268. import guide from "./guide.vue";
  269. import liveBroadcast from "./liveBroadcast.vue";
  270. import menus from "./menus.vue";
  271. import news from "./news.vue";
  272. import pictureCube from "./pictureCube.vue";
  273. import promotionList from "./promotionList.vue";
  274. import seckill from "./seckill.vue";
  275. import swiperBg from "./swiperBg.vue";
  276. import swipers from "./swipers.vue";
  277. import titles from "./titles.vue";
  278. import presale from "./presale.vue";
  279. import pointsMall from "./pointsMall.vue";
  280. import richText from "./richText.vue";
  281. import videos from "./videos.vue";
  282. import signIn from "./signIn.vue";
  283. import hotspot from "./hotspot.vue";
  284. import follow from "./follow.vue";
  285. import productInfo from "./productInfo.vue";
  286. import homePaidVip from "./homePaidVip.vue";
  287. import homeProductService from "./homeProductService.vue";
  288. import homeReviews from "./homeReviews.vue";
  289. import productDesc from "./productDesc.vue";
  290. import customComponent from "./customComponent.vue";
  291. export default {
  292. name: "PageDesign",
  293. components: {
  294. pageFooter,
  295. homeComb,
  296. headerSerch,
  297. tabNav,
  298. userInfor,
  299. homeUserInfor,
  300. newVip,
  301. articleList,
  302. bargain,
  303. blankPage,
  304. combination,
  305. coupon,
  306. customerService,
  307. goodList,
  308. guide,
  309. liveBroadcast,
  310. menus,
  311. news,
  312. pictureCube,
  313. promotionList,
  314. seckill,
  315. swiperBg,
  316. swipers,
  317. titles,
  318. presale,
  319. pointsMall,
  320. richText,
  321. videos,
  322. signIn,
  323. hotspot,
  324. follow,
  325. productInfo,
  326. homePaidVip,
  327. homeProductService,
  328. homeReviews,
  329. productDesc,
  330. customComponent,
  331. },
  332. mixins: [colors],
  333. props: {
  334. // DIY配置数据
  335. diyData: {
  336. type: Object,
  337. default: () => ({}),
  338. },
  339. // 是否为首页(用于控制小程序添加到我的小程序提示等)
  340. isHome: {
  341. type: Boolean,
  342. default: false,
  343. },
  344. // 页面滚动状态
  345. isScrolled: {
  346. type: Boolean,
  347. default: false,
  348. },
  349. // 是否固定(用于吸顶)
  350. isFixed: {
  351. type: Boolean,
  352. default: false,
  353. },
  354. productData: {
  355. type: Object,
  356. default: () => ({}),
  357. },
  358. priceData: {
  359. type: Object,
  360. default: () => ({}),
  361. },
  362. skuList: {
  363. type: Array,
  364. default: () => [],
  365. },
  366. reply: {
  367. type: Array,
  368. default: () => [],
  369. },
  370. replyCount: {
  371. type: Number,
  372. default: 0,
  373. },
  374. replyChance: {
  375. type: String | Number,
  376. default: 0,
  377. },
  378. productId: {
  379. type: Number | String,
  380. default: 0,
  381. },
  382. goodList: {
  383. type: Array,
  384. default: () => [],
  385. },
  386. // 视频播放状态
  387. productVideoStatus: {
  388. type: Boolean,
  389. default: false,
  390. },
  391. // 进店规则归属门店排序位置
  392. belongIndex: {
  393. type: Number,
  394. default: 0,
  395. },
  396. // 网络错误状态
  397. errorNetwork: {
  398. type: Boolean,
  399. default: false,
  400. },
  401. couponList: {
  402. type: Array,
  403. default: () => [],
  404. },
  405. activity: {
  406. type: Array,
  407. default: () => [],
  408. },
  409. attr: {
  410. type: Object,
  411. default: () => ({}),
  412. },
  413. attrTxt: {
  414. type: String,
  415. default: "",
  416. },
  417. attrValue: {
  418. type: String,
  419. default: "",
  420. },
  421. // 微页面
  422. microPage: {
  423. type: Boolean,
  424. default: false,
  425. },
  426. // 商品 vip 模块
  427. isShowPaidVip: {
  428. type: Boolean,
  429. default: false,
  430. },
  431. },
  432. data() {
  433. return {
  434. styleConfig: [],
  435. homeCombData: {},
  436. headerSerchCombData: {},
  437. cateNavData: {},
  438. footerConfigData: null,
  439. showHomeComb: false,
  440. isHeaderSerch: false,
  441. showCateNav: false,
  442. bgColor: "",
  443. bgPic: "",
  444. bgTabVal: "",
  445. positionTop: 0,
  446. isFooter: false,
  447. pdHeight: 0,
  448. myApplet: true,
  449. getHeight: this.$util.getWXStatusHeight(),
  450. imgHost: HTTP_REQUEST_URL,
  451. };
  452. },
  453. computed: {
  454. // #ifdef MP
  455. appletStyle() {
  456. return {
  457. top: this.getHeight.menuButtonInfo.bottom + 8 + "px",
  458. right: "10px",
  459. };
  460. },
  461. // #endif
  462. pageStyle() {
  463. return {
  464. backgroundColor: this.bgColor,
  465. backgroundImage: this.bgPic ? `url(${this.bgPic})` : "",
  466. minHeight: "100vh", // 确保背景铺满
  467. };
  468. },
  469. bgClass() {
  470. if (this.bgTabVal == 2) return "fullsize noRepeat";
  471. if (this.bgTabVal == 1) return "repeat ysize";
  472. return "noRepeat ysize";
  473. },
  474. pdHeights() {
  475. let H = `${this.pdHeight * 2 + 100}rpx`;
  476. return {
  477. height: this.isFooter ? H : "100rpx",
  478. };
  479. },
  480. },
  481. watch: {
  482. diyData: {
  483. handler(val) {
  484. if (val && Object.keys(val).length > 0) {
  485. this.setDiyData(val);
  486. } else {
  487. // 重置数据
  488. this.styleConfig = [];
  489. this.homeCombData = {};
  490. this.headerSerchCombData = {};
  491. this.cateNavData = {};
  492. this.footerConfigData = null;
  493. this.showHomeComb = false;
  494. this.isHeaderSerch = false;
  495. this.showCateNav = false;
  496. this.bgColor = "";
  497. this.bgPic = "";
  498. this.bgTabVal = "";
  499. }
  500. },
  501. deep: true,
  502. immediate: true,
  503. },
  504. },
  505. methods: {
  506. reconnect() {
  507. this.$emit("reconnect");
  508. },
  509. onChangeSpec(item) {
  510. this.$emit("changeSpec", item);
  511. },
  512. onShowSpecModal() {
  513. this.$emit("showSpecModal");
  514. },
  515. onShowCoupon() {
  516. this.$emit("showCoupon");
  517. },
  518. onOpenModal(type) {
  519. this.$emit("openModal", type);
  520. },
  521. onGoActivity(item) {
  522. this.$emit("goActivity", item);
  523. },
  524. onShare() {
  525. this.$emit("share");
  526. },
  527. // 对象转数组
  528. objToArr(data) {
  529. if (!data) return [];
  530. let obj = Object.keys(data).sort();
  531. let m = obj.map((key) => data[key]);
  532. return m;
  533. },
  534. setDiyData(data) {
  535. if (!data) return;
  536. if (data.is_bg_color) {
  537. this.bgColor = data.color_picker;
  538. }
  539. if (data.is_bg_pic) {
  540. this.bgPic = data.bg_pic;
  541. this.bgTabVal = data.bg_tab_val;
  542. }
  543. let temp = [];
  544. // 重置状态
  545. this.showHomeComb = false;
  546. this.isHeaderSerch = false;
  547. this.showCateNav = false;
  548. this.footerConfigData = null;
  549. if (data.value) {
  550. let lastArr = this.objToArr(data.value);
  551. lastArr.forEach((item) => {
  552. if (item.name == "pageFoot" && !this.microPage) {
  553. this.footerConfigData = item;
  554. }
  555. if (item.name === "homeComb" && !item.isHide) {
  556. this.showHomeComb = true;
  557. this.homeCombData = item;
  558. if (item.searchConfig && item.searchConfig.tabVal) {
  559. this.positionTop = uni.getWindowInfo().statusBarHeight + 43;
  560. }
  561. }
  562. if (item.name == "headerSerch" && !item.isHide) {
  563. this.isHeaderSerch = true;
  564. this.headerSerchCombData = item;
  565. }
  566. if (item.name == "tabNav" && !item.isHide) {
  567. this.showCateNav = true;
  568. this.cateNavData = item;
  569. }
  570. if (!item.isHide) {
  571. temp.push(item);
  572. }
  573. });
  574. // 排序
  575. temp.sort((a, b) => a.timestamp - b.timestamp);
  576. this.styleConfig = temp;
  577. }
  578. },
  579. bindSortId(data) {
  580. this.$emit("bindSortId", data);
  581. },
  582. bindHeight(data) {
  583. this.$emit("bindHeight", data);
  584. },
  585. storeTap(id) {
  586. this.$emit("storeTap", id);
  587. },
  588. changeLogin() {
  589. this.$emit("changeLogin");
  590. },
  591. changeBarg(item) {
  592. this.$emit("changeBarg", item);
  593. },
  594. newDataStatus(val, num) {
  595. this.isFooter = val ? true : false;
  596. this.pdHeight = num;
  597. this.$emit("newDataStatus", { val, num });
  598. },
  599. reconnect() {
  600. this.$emit("reconnect");
  601. },
  602. },
  603. };
  604. </script>
  605. <style lang="scss" scoped>
  606. .page-design {
  607. overflow-y: scroll;
  608. overflow-x: hidden;
  609. min-height: 100vh;
  610. }
  611. .myApplet {
  612. position: relative;
  613. &::after {
  614. position: absolute;
  615. right: 55px;
  616. top: -5px;
  617. content: "";
  618. width: 0;
  619. height: 0;
  620. border-left: 7px solid transparent;
  621. border-right: 7px solid transparent;
  622. border-bottom: 7px solid #fff;
  623. }
  624. }
  625. .ysize {
  626. background-size: 100%;
  627. }
  628. .fullsize {
  629. background-size: 100% 100%;
  630. }
  631. .repeat {
  632. background-repeat: repeat;
  633. }
  634. .noRepeat {
  635. background-repeat: no-repeat;
  636. }
  637. .error-network {
  638. position: fixed;
  639. left: 0;
  640. top: 0;
  641. display: flex;
  642. flex-direction: column;
  643. align-items: center;
  644. width: 100%;
  645. height: 100%;
  646. padding-top: 40rpx;
  647. background: #fff;
  648. image {
  649. width: 414rpx;
  650. height: 336rpx;
  651. }
  652. .title {
  653. position: relative;
  654. top: -40rpx;
  655. font-size: 32rpx;
  656. color: #666;
  657. }
  658. .btn {
  659. display: flex;
  660. align-items: center;
  661. justify-content: center;
  662. width: 508rpx;
  663. height: 86rpx;
  664. margin-top: 100rpx;
  665. border: 1px solid #d74432;
  666. color: #e93323;
  667. font-size: 30rpx;
  668. border-radius: 120rpx;
  669. }
  670. }
  671. </style>