customComponent.vue 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. <template>
  2. <view class="custom-box">
  3. <common-wrapper :config="configObj">
  4. <!-- Unified List Mode -->
  5. <view
  6. v-if="
  7. ['article', 'coupon', 'goods'].includes(selectTypeValue) &&
  8. dataList.length > 0
  9. "
  10. >
  11. <view class="custom-box-list" :style="[getListContainerStyle]">
  12. <view
  13. v-for="(dataItem, dataIndex) in dataList"
  14. :key="dataIndex"
  15. :style="[getListItemStyle]"
  16. class="list-item"
  17. >
  18. <view class="home_custom_component" :style="[getDataWrapperStyle]">
  19. <view :style="[itemWrapperStyle]">
  20. <view
  21. v-for="item in visibleComponents"
  22. :key="item.id"
  23. :style="[getComponentStyle(item.style)]"
  24. @click="goDetail(dataItem, item)"
  25. >
  26. <!-- Picture -->
  27. <image
  28. v-if="item.component === 'Picture'"
  29. :src="getPictureUrl(item, dataItem)"
  30. :style="[getPictureStyle(item.propValue)]"
  31. mode="aspectFill"
  32. ></image>
  33. <!-- Text -->
  34. <view
  35. v-else-if="item.component === 'Text'"
  36. :style="[getTextBgStyle(item.propValue)]"
  37. >
  38. <view
  39. :style="[getTextStyle(item.propValue)]"
  40. :class="
  41. item.propValue.ellipsis == 1
  42. ? 'line1'
  43. : item.propValue.ellipsis == 2
  44. ? 'line2'
  45. : item.propValue.ellipsis == 3
  46. ? 'line3'
  47. : ''
  48. "
  49. >
  50. {{ getDisplayText(item, dataItem) }}
  51. </view>
  52. </view>
  53. <!-- Icon -->
  54. <view
  55. v-else-if="item.component === 'Icon'"
  56. :style="[getIconStyle(item.propValue)]"
  57. class="flex-center"
  58. >
  59. <text
  60. class="iconfont"
  61. :class="item.propValue.class"
  62. :style="{
  63. fontSize:
  64. item.propValue.size * 2 * contentScale + 'rpx',
  65. color: item.propValue.color,
  66. }"
  67. ></text>
  68. </view>
  69. <!-- Line -->
  70. <view
  71. v-else-if="item.component === 'Line'"
  72. class="flex-center"
  73. style="width: 100%; height: 100%"
  74. >
  75. <view :style="[getLineStyle(item.propValue)]"></view>
  76. </view>
  77. <!-- Panel -->
  78. <view
  79. v-else-if="item.component === 'Panel'"
  80. :style="[getPanelStyle(item.propValue)]"
  81. ></view>
  82. </view>
  83. </view>
  84. </view>
  85. </view>
  86. </view>
  87. </view>
  88. <!-- Default Mode (User/Single) -->
  89. <view v-else style="width: 100%">
  90. <view class="home_custom_component" :style="[getDataWrapperStyle]">
  91. <view :style="[itemWrapperStyle]">
  92. <view
  93. v-for="item in visibleComponents"
  94. :key="item.id"
  95. :style="[getComponentStyle(item.style)]"
  96. >
  97. <!-- Picture -->
  98. <image
  99. v-if="item.component === 'Picture'"
  100. :src="getPictureUrl(item, userInfo)"
  101. :style="[getPictureStyle(item.propValue)]"
  102. mode="aspectFill"
  103. @click="handleUserClick(item)"
  104. ></image>
  105. <!-- Text -->
  106. <view
  107. v-else-if="item.component === 'Text'"
  108. :style="[getTextBgStyle(item.propValue)]"
  109. >
  110. <view
  111. :style="[getTextStyle(item.propValue)]"
  112. :class="
  113. item.propValue.ellipsis == 1
  114. ? 'line1'
  115. : item.propValue.ellipsis == 2
  116. ? 'line2'
  117. : item.propValue.ellipsis == 3
  118. ? 'line3'
  119. : ''
  120. "
  121. @click="handleUserClick(item)"
  122. >
  123. {{ getDisplayText(item, userInfo) }}
  124. </view>
  125. </view>
  126. <!-- Icon -->
  127. <view
  128. v-else-if="item.component === 'Icon'"
  129. :style="[getIconStyle(item.propValue)]"
  130. class="flex-center"
  131. @click="handleUserClick(item)"
  132. >
  133. <text
  134. class="iconfont"
  135. :class="item.propValue.class"
  136. :style="{
  137. fontSize: item.propValue.size * 2 * contentScale + 'rpx',
  138. color: item.propValue.color,
  139. }"
  140. ></text>
  141. </view>
  142. <!-- Line -->
  143. <view
  144. v-else-if="item.component === 'Line'"
  145. class="flex-center"
  146. style="width: 100%; height: 100%"
  147. >
  148. <view :style="[getLineStyle(item.propValue)]"></view>
  149. </view>
  150. <!-- Panel -->
  151. <view
  152. v-else-if="item.component === 'Panel'"
  153. :style="[getPanelStyle(item.propValue)]"
  154. @click="handleUserClick(item)"
  155. ></view>
  156. </view>
  157. </view>
  158. </view>
  159. </view>
  160. </common-wrapper>
  161. </view>
  162. </template>
  163. <script>
  164. import {
  165. getThemeArticle,
  166. getThemeCoupon,
  167. getThemeProduct,
  168. getThemeUser,
  169. setCouponReceive,
  170. } from "@/api/api.js";
  171. import { mapGetters } from "vuex";
  172. import CommonWrapper from "./commonWrapper.vue";
  173. export default {
  174. name: "customComponent",
  175. components: {
  176. CommonWrapper,
  177. },
  178. props: {
  179. dataConfig: {
  180. type: Object,
  181. default: () => ({}),
  182. },
  183. },
  184. data() {
  185. return {
  186. dataList: [],
  187. articleList: [],
  188. couponList: [],
  189. goodsList: [],
  190. userInfo: {},
  191. };
  192. },
  193. computed: {
  194. ...mapGetters(["isLogin"]),
  195. configObj() {
  196. return this.dataConfig;
  197. },
  198. customComponents() {
  199. return this.configObj.customComponents || {};
  200. },
  201. visibleComponents() {
  202. if (!this.customComponents || !this.customComponents.list) return [];
  203. return this.customComponents.list.filter((item) => !item.isHidden);
  204. },
  205. contentScale() {
  206. const {
  207. marginConfig,
  208. paddingConfig,
  209. borderConfig,
  210. paddingDataConfig,
  211. borderDataConfig,
  212. marginDataConfig,
  213. } = this.configObj;
  214. let width = 375;
  215. // Wrapper Margin
  216. if (marginConfig) {
  217. if (!marginConfig.isAll) {
  218. width -= (marginConfig.val || 0) * 2;
  219. } else {
  220. width -=
  221. (marginConfig.valList[1].val || 0) +
  222. (marginConfig.valList[3].val || 0);
  223. }
  224. }
  225. // Wrapper Padding
  226. if (paddingConfig) {
  227. if (!paddingConfig.isAll) {
  228. width -= (paddingConfig.val || 0) * 2;
  229. } else {
  230. width -=
  231. (paddingConfig.valList[1].val || 0) +
  232. (paddingConfig.valList[3].val || 0);
  233. }
  234. }
  235. // Wrapper Border
  236. if (borderConfig && borderConfig.tabVal) {
  237. const borderWidth = (borderConfig.widthConfig.val || 0) * 2;
  238. width -= borderWidth;
  239. }
  240. // Data Margin
  241. if (marginDataConfig) {
  242. if (!marginDataConfig.isAll) {
  243. width -= (marginDataConfig.val || 0) * 2;
  244. } else {
  245. width -=
  246. (marginDataConfig.valList[1].val || 0) +
  247. (marginDataConfig.valList[3].val || 0);
  248. }
  249. }
  250. // Data Padding
  251. if (paddingDataConfig) {
  252. if (!paddingDataConfig.isAll) {
  253. width -= (paddingDataConfig.val || 0) * 2;
  254. } else {
  255. width -=
  256. (paddingDataConfig.valList[1].val || 0) +
  257. (paddingDataConfig.valList[3].val || 0);
  258. }
  259. }
  260. // Data Border
  261. if (borderDataConfig && borderDataConfig.tabVal) {
  262. const borderWidth = (borderDataConfig.widthConfig.val || 0) * 2;
  263. width -= borderWidth;
  264. }
  265. return width / 375;
  266. },
  267. currentDisplayMode() {
  268. if (this.selectTypeValue === "article")
  269. return this.configObj.articleDisplayMode;
  270. if (this.selectTypeValue === "coupon")
  271. return this.configObj.couponDisplayMode;
  272. if (this.selectTypeValue === "goods")
  273. return this.configObj.goodsDisplayMode;
  274. return null;
  275. },
  276. currentColumnStyle() {
  277. if (this.selectTypeValue === "article")
  278. return this.configObj.articleColumnStyle;
  279. if (this.selectTypeValue === "coupon")
  280. return this.configObj.couponColumnStyle;
  281. if (this.selectTypeValue === "goods")
  282. return this.configObj.goodsColumnStyle;
  283. return null;
  284. },
  285. selectTypeValue() {
  286. return this.configObj.selectType
  287. ? this.configObj.selectType.activeValue
  288. : "user";
  289. },
  290. // Background Gradient
  291. getDataWrapperStyle() {
  292. const {
  293. filletDataConfig,
  294. marginDataConfig,
  295. paddingDataConfig,
  296. componentBgDataConfig,
  297. borderDataConfig,
  298. shadowDataConfig,
  299. } = this.configObj;
  300. const style = {};
  301. // Border Radius
  302. if (filletDataConfig) {
  303. if (filletDataConfig.type) {
  304. style.borderRadius = `${filletDataConfig.valList[0].val}px ${filletDataConfig.valList[1].val}px ${filletDataConfig.valList[3].val}px ${filletDataConfig.valList[2].val}px`;
  305. } else {
  306. style.borderRadius = `${filletDataConfig.val}px`;
  307. }
  308. }
  309. // Margin
  310. if (marginDataConfig) {
  311. if (marginDataConfig.isAll) {
  312. style.marginTop = `${marginDataConfig.valList[0].val}px`;
  313. style.marginRight = `${marginDataConfig.valList[1].val}px`;
  314. style.marginBottom = `${marginDataConfig.valList[2].val}px`;
  315. style.marginLeft = `${marginDataConfig.valList[3].val}px`;
  316. } else {
  317. style.margin = `${marginDataConfig.val}px`;
  318. }
  319. }
  320. // Padding
  321. if (paddingDataConfig) {
  322. if (paddingDataConfig.isAll) {
  323. style.paddingTop = `${paddingDataConfig.valList[0].val}px`;
  324. style.paddingRight = `${paddingDataConfig.valList[1].val}px`;
  325. style.paddingBottom = `${paddingDataConfig.valList[2].val}px`;
  326. style.paddingLeft = `${paddingDataConfig.valList[3].val}px`;
  327. } else {
  328. style.padding = `${paddingDataConfig.val}px`;
  329. }
  330. }
  331. // Background
  332. if (componentBgDataConfig) {
  333. if (componentBgDataConfig.tabVal === 0) {
  334. // Color
  335. const colorList = componentBgDataConfig.colorConfig.color;
  336. if (colorList && colorList.length > 0) {
  337. style.background = `linear-gradient(${
  338. componentBgDataConfig.colorDirection.tabVal === 0
  339. ? "90deg"
  340. : componentBgDataConfig.colorDirection.tabVal === 1
  341. ? "180deg"
  342. : componentBgDataConfig.colorDirection.tabVal === 2
  343. ? "135deg"
  344. : "45deg"
  345. }, ${colorList[0].item} 0%, ${colorList[1] ? colorList[1].item : colorList[0].item} 100%)`;
  346. }
  347. } else {
  348. // Image
  349. if (componentBgDataConfig.imageConfig.url) {
  350. style.backgroundImage = `url(${componentBgDataConfig.imageConfig.url})`;
  351. style.backgroundSize = "100% 100%";
  352. style.backgroundRepeat = "no-repeat";
  353. }
  354. }
  355. }
  356. // Border
  357. if (borderDataConfig && borderDataConfig.tabVal) {
  358. style.borderStyle =
  359. borderDataConfig.styleConfig.tabVal === 0
  360. ? "solid"
  361. : borderDataConfig.styleConfig.tabVal === 1
  362. ? "dashed"
  363. : "dotted";
  364. style.borderWidth = `${borderDataConfig.widthConfig.val}px`;
  365. style.borderColor = borderDataConfig.colorConfig.color[0].item;
  366. }
  367. // Shadow
  368. if (shadowDataConfig && shadowDataConfig.tabVal) {
  369. style.boxShadow = `${shadowDataConfig.xConfig.val}px ${shadowDataConfig.yConfig.val}px ${shadowDataConfig.blurConfig.val}px ${shadowDataConfig.spreadConfig.val}px ${shadowDataConfig.colorConfig.color[0].item}`;
  370. }
  371. return style;
  372. },
  373. itemWrapperStyle() {
  374. // let bgColorLeft = this.configObj.moduleColor.color[0].item;
  375. // let bgColorRight = this.configObj.moduleColor.color[1].item;
  376. const {
  377. marginConfig,
  378. paddingConfig,
  379. borderConfig,
  380. marginDataConfig,
  381. paddingDataConfig,
  382. borderDataConfig,
  383. } = this.configObj;
  384. let marginHeight = 0;
  385. let paddingHeight = 0;
  386. let borderWidth = 0;
  387. // Wrapper Margin
  388. if (marginConfig) {
  389. marginHeight += marginConfig.isAll
  390. ? marginConfig.valList[0].val + marginConfig.valList[2].val || 0
  391. : marginConfig.val || 0;
  392. }
  393. // Wrapper Padding
  394. if (paddingConfig) {
  395. paddingHeight += paddingConfig.isAll
  396. ? paddingConfig.valList[0].val + paddingConfig.valList[2].val || 0
  397. : paddingConfig.val || 0;
  398. }
  399. // Wrapper Border
  400. if (borderConfig && borderConfig.tabVal) {
  401. borderWidth += borderConfig.widthConfig.val / 2;
  402. }
  403. // Data Margin
  404. if (marginDataConfig) {
  405. marginHeight += marginDataConfig.isAll
  406. ? marginDataConfig.valList[0].val + marginDataConfig.valList[2].val ||
  407. 0
  408. : marginDataConfig.val || 0;
  409. }
  410. // Data Padding
  411. if (paddingDataConfig) {
  412. paddingHeight += paddingDataConfig.isAll
  413. ? paddingDataConfig.valList[0].val +
  414. paddingDataConfig.valList[2].val || 0
  415. : paddingDataConfig.val || 0;
  416. }
  417. // Data Border
  418. if (borderDataConfig && borderDataConfig.tabVal) {
  419. borderWidth += borderDataConfig.widthConfig.val / 2;
  420. }
  421. const scale = this.contentScale;
  422. const canvasHeight =
  423. (this.customComponents.canvasHeight * scale || 0) +
  424. borderWidth * scale +
  425. marginHeight * scale +
  426. paddingHeight * scale;
  427. return {
  428. height: this.customComponents.canvasHeight * scale * 2 + "rpx",
  429. position: "relative",
  430. width: "100%",
  431. // background: `linear-gradient(90deg,${bgColorLeft || "#fff"} 0%,${
  432. // bgColorRight || "#fff"
  433. // } 100%)`,
  434. };
  435. },
  436. // List Styles
  437. getListContainerStyle() {
  438. let displayMode, columnStyle;
  439. if (this.selectTypeValue === "article") {
  440. displayMode = this.configObj.articleDisplayMode;
  441. columnStyle = this.configObj.articleColumnStyle;
  442. } else if (this.selectTypeValue === "coupon") {
  443. displayMode = this.configObj.couponDisplayMode;
  444. columnStyle = this.configObj.couponColumnStyle;
  445. } else if (this.selectTypeValue === "goods") {
  446. displayMode = this.configObj.goodsDisplayMode;
  447. columnStyle = this.configObj.goodsColumnStyle;
  448. }
  449. const style = {
  450. position: "relative",
  451. width: "100%",
  452. };
  453. if (displayMode && displayMode.tabVal === 1) {
  454. // Horizontal Scroll
  455. style.display = "flex";
  456. style.overflowX = "auto";
  457. style.overflowY = "hidden";
  458. style.flexWrap = "nowrap";
  459. } else {
  460. style.display = "flex";
  461. style.flexWrap = "wrap";
  462. }
  463. return style;
  464. },
  465. getListItemStyle() {
  466. let displayMode, columnStyle;
  467. if (this.selectTypeValue === "article") {
  468. displayMode = this.configObj.articleDisplayMode;
  469. columnStyle = this.configObj.articleColumnStyle;
  470. } else if (this.selectTypeValue === "coupon") {
  471. displayMode = this.configObj.couponDisplayMode;
  472. columnStyle = this.configObj.couponColumnStyle;
  473. } else if (this.selectTypeValue === "goods") {
  474. displayMode = this.configObj.goodsDisplayMode;
  475. columnStyle = this.configObj.goodsColumnStyle;
  476. }
  477. const style = {
  478. width: "100%",
  479. };
  480. if (displayMode && displayMode.tabVal === 1) {
  481. style.flexShrink = 0;
  482. if (columnStyle) {
  483. const colCount = columnStyle.tabVal + 1;
  484. style.width = `${100 / colCount}%`;
  485. } else {
  486. style.width = "400rpx";
  487. }
  488. } else {
  489. if (columnStyle) {
  490. const colCount = columnStyle.tabVal + 1;
  491. style.width = `${100 / colCount}%`;
  492. }
  493. }
  494. return style;
  495. },
  496. // Add flex-basis for multi-column layouts to ensure wrapping works correctly if needed,
  497. // though width usually suffices.
  498. },
  499. watch: {
  500. dataConfig: {
  501. handler(val) {
  502. this.fetchData();
  503. },
  504. deep: true,
  505. immediate: true,
  506. },
  507. },
  508. methods: {
  509. fetchData() {
  510. if (this.selectTypeValue === "user") {
  511. this.fetchUserInfo();
  512. this.dataList = [];
  513. return;
  514. }
  515. if (this.selectTypeValue === "article") {
  516. this.fetchArticleData();
  517. } else if (this.selectTypeValue === "coupon") {
  518. this.fetchCouponData();
  519. } else if (this.selectTypeValue === "goods") {
  520. this.fetchGoodsData();
  521. }
  522. },
  523. fetchArticleData() {
  524. if (!this.configObj.articleDataSource) return;
  525. let params = {
  526. limit: this.configObj.articleNum.val,
  527. order: this.configObj.articleSort.tabVal,
  528. sort: this.configObj.articleSortRule.tabVal,
  529. };
  530. if (this.configObj.articleDataSource.tabVal === 0) {
  531. // Specific Data
  532. if (!this.configObj.articleList || !this.configObj.articleList.list)
  533. return;
  534. params.ids = this.configObj.articleList.list
  535. .map((item) => item.id)
  536. .join(",");
  537. if (params.ids.length === 0) {
  538. this.dataList = [];
  539. return;
  540. }
  541. } else {
  542. // Filter Data
  543. params.cid = Array.isArray(this.configObj.articleClass.activeValue)
  544. ? this.configObj.articleClass.activeValue.join(",")
  545. : this.configObj.articleClass.activeValue;
  546. }
  547. getThemeArticle(params).then((res) => {
  548. this.dataList = res.data;
  549. });
  550. },
  551. fetchCouponData() {
  552. if (!this.configObj.couponDataSource) return;
  553. let params = {
  554. limit: this.configObj.couponNum.val,
  555. order: this.configObj.couponSort.tabVal,
  556. sort: this.configObj.couponSortRule.tabVal,
  557. };
  558. if (this.configObj.couponDataSource.tabVal === 0) {
  559. // Specific Data
  560. if (!this.configObj.couponList || !this.configObj.couponList.list)
  561. return;
  562. params.ids = this.configObj.couponList.list
  563. .map((item) => item.id)
  564. .join(",");
  565. if (params.ids.length === 0) {
  566. this.dataList = [];
  567. return;
  568. }
  569. } else {
  570. // Filter Data
  571. params.type = this.configObj.couponType.activeValue;
  572. params.user_type = this.configObj.couponUserType.activeValue;
  573. if (this.configObj.couponUserType.activeValue != 2) {
  574. params.send_type = this.configObj.couponSendType.activeValue;
  575. }
  576. params.is_min_price = this.configObj.couponThreshold.tabVal;
  577. if (params.is_min_price == 1) {
  578. params.min_price = this.configObj.couponThresholdValue.val;
  579. }
  580. if (
  581. this.configObj.couponTime.val &&
  582. this.configObj.couponTime.val.length
  583. ) {
  584. params.start_time = this.configObj.couponTime.val[0];
  585. params.end_time = this.configObj.couponTime.val[1];
  586. }
  587. }
  588. getThemeCoupon(params).then((res) => {
  589. this.dataList = res.data;
  590. });
  591. },
  592. fetchGoodsData() {
  593. if (!this.configObj.goodsDataSource) return;
  594. let params = {
  595. limit: this.configObj.goodsNum.val,
  596. order: this.configObj.goodsSort.tabVal,
  597. sort: this.configObj.goodsSortRule.tabVal,
  598. };
  599. if (this.configObj.goodsDataSource.tabVal === 0) {
  600. // Specific Data
  601. if (!this.configObj.goodsList || !this.configObj.goodsList.list) return;
  602. params.ids = this.configObj.goodsList.list
  603. .map((item) => item.id)
  604. .join(",");
  605. if (params.ids.length === 0) {
  606. this.dataList = [];
  607. return;
  608. }
  609. } else {
  610. // Filter Data
  611. params.cate_ids = Array.isArray(this.configObj.goodsClass.activeValue)
  612. ? this.configObj.goodsClass.activeValue.join(",")
  613. : this.configObj.goodsClass.activeValue;
  614. }
  615. getThemeProduct(params).then((res) => {
  616. this.dataList = res.data;
  617. });
  618. },
  619. fetchUserInfo() {
  620. getThemeUser().then((res) => {
  621. this.userInfo = res.data || {};
  622. });
  623. },
  624. getWrapperStyle(displayMode, columnStyle) {
  625. const style = {
  626. width: "100%",
  627. };
  628. if (displayMode && displayMode.tabVal === 1) {
  629. style.flexShrink = 0;
  630. if (columnStyle) {
  631. const colCount = columnStyle.tabVal + 1;
  632. style.width = `${100 / colCount}%`;
  633. } else {
  634. style.width = "200px";
  635. }
  636. } else {
  637. if (columnStyle) {
  638. const colCount = columnStyle.tabVal + 1;
  639. style.width = `${100 / colCount}%`;
  640. }
  641. }
  642. return style;
  643. },
  644. getComponentStyle(style) {
  645. const scale = this.contentScale;
  646. return {
  647. position: "absolute",
  648. left: style.left * scale * 2 + "rpx",
  649. top: style.top * scale * 2 + "rpx",
  650. width: style.width * scale * 2 + "rpx",
  651. maxWidth: "100%",
  652. height: style.height * scale * 2 + "rpx",
  653. zIndex: style.zIndex,
  654. transform: `rotate(${style.rotate || 0}deg)`,
  655. };
  656. },
  657. getPictureStyle(propValue) {
  658. let borderRadius = 0;
  659. if (propValue.isRadiusAll) {
  660. borderRadius = propValue.borderRadius * 2 + "rpx";
  661. } else {
  662. borderRadius = `${propValue.borderRadiusTopLeft * 2}rpx ${
  663. propValue.borderRadiusTopRight * 2
  664. }rpx ${propValue.borderRadiusBottomRight * 2}rpx ${
  665. propValue.borderRadiusBottomLeft * 2
  666. }rpx`;
  667. }
  668. const style = {
  669. width: "100%",
  670. height: "100%",
  671. borderRadius: borderRadius,
  672. };
  673. if (propValue.showShadow) {
  674. style.boxShadow = `${propValue.shadowX * 2}rpx ${
  675. propValue.shadowY * 2
  676. }rpx ${propValue.shadowBlur * 2}rpx ${propValue.shadowSpread * 2}rpx ${
  677. propValue.shadowColor
  678. }`;
  679. }
  680. if (propValue.showBorder) {
  681. style.border = `${propValue.borderWidth * 2}rpx ${
  682. propValue.borderStyle
  683. } ${propValue.borderColor}`;
  684. }
  685. return style;
  686. },
  687. getTextStyle(propValue) {
  688. const scale = this.contentScale;
  689. const style = {
  690. width: "100%",
  691. fontSize: propValue.fontSize * scale * 2 + "rpx",
  692. color: propValue.color,
  693. lineHeight: propValue.lineHeight,
  694. letterSpacing: (propValue.letterSpacing || 0) * scale * 2 + "rpx",
  695. fontWeight: propValue.fontWeight || "normal",
  696. fontStyle: propValue.fontStyle || "normal",
  697. textDecoration: propValue.textDecoration || "none",
  698. textAlign: propValue.textAlign || "left",
  699. boxSizing: "border-box",
  700. wordBreak: "break-all",
  701. };
  702. if (propValue.ellipsis == 0) {
  703. style.whiteSpace = "normal";
  704. }
  705. if (propValue.showTextShadow) {
  706. style.textShadow = `${(propValue.shadowX || 0) * scale * 2}rpx ${(propValue.shadowY || 0) * scale * 2}rpx ${(propValue.shadowBlur || 0) * scale * 2}rpx ${
  707. propValue.shadowColor || "rgba(0,0,0,0.5)"
  708. }`;
  709. }
  710. return style;
  711. },
  712. getTextBgStyle(propValue) {
  713. const scale = this.contentScale;
  714. const style = {
  715. height: "100%",
  716. padding: `${(propValue.paddingTop || 0) * scale * 2}rpx ${(propValue.paddingRight || 0) * scale * 2}rpx ${
  717. (propValue.paddingBottom || 0) * scale * 2
  718. }rpx ${(propValue.paddingLeft || 0) * scale * 2}rpx`,
  719. borderWidth:
  720. (propValue.showBorder ? propValue.borderWidth * scale * 2 : 0) + "rpx",
  721. borderColor: propValue.borderColor || "transparent",
  722. borderStyle: propValue.borderStyle || "solid",
  723. };
  724. // Background
  725. if (propValue.bgColor2) {
  726. const directionMap = {
  727. horizontal: "90deg",
  728. vertical: "180deg",
  729. "left-diagonal": "135deg",
  730. "right-diagonal": "45deg",
  731. };
  732. const deg = directionMap[propValue.bgDirection] || "180deg";
  733. style.backgroundImage = `linear-gradient(${deg}, ${propValue.backgroundColor || "transparent"}, ${
  734. propValue.bgColor2
  735. })`;
  736. } else {
  737. style.backgroundColor = propValue.backgroundColor || "transparent";
  738. }
  739. if (propValue.showBorder && propValue.borderWidth) {
  740. style.backgroundSize = `100% calc(100% + ${propValue.borderWidth * scale * 4 || 0}rpx)`;
  741. style.backgroundPosition = `0rpx -${propValue.borderWidth * scale * 2 || 0}rpx`;
  742. }
  743. // Border Radius
  744. if (propValue.isRadiusAll) {
  745. style.borderRadius = (propValue.borderRadius || 0) * scale * 2 + "rpx";
  746. } else {
  747. style.borderRadius = `${(propValue.borderRadiusTopLeft || 0) * scale * 2}rpx ${(propValue.borderRadiusTopRight || 0) * scale * 2}rpx ${
  748. (propValue.borderRadiusBottomRight || 0) * scale * 2
  749. }rpx ${(propValue.borderRadiusBottomLeft || 0) * scale * 2}rpx`;
  750. }
  751. return style;
  752. },
  753. getIconStyle(propValue) {
  754. const scale = this.contentScale;
  755. const style = {
  756. display: "flex",
  757. justifyContent: propValue.iconAlign || "center",
  758. alignItems: "center",
  759. width: "100%",
  760. height: "100%",
  761. boxSizing: "border-box",
  762. borderWidth:
  763. (propValue.showBorder ? propValue.borderWidth * scale * 2 : 0) + "rpx",
  764. borderColor: propValue.borderColor || "transparent",
  765. borderStyle: propValue.borderStyle || "solid",
  766. padding: `${(propValue.paddingTop || 0) * scale * 2}rpx ${(propValue.paddingRight || 0) * scale * 2}rpx ${
  767. (propValue.paddingBottom || 0) * scale * 2
  768. }rpx ${(propValue.paddingLeft || 0) * scale * 2}rpx`,
  769. };
  770. // Background
  771. if (propValue.bgColor2) {
  772. const directionMap = {
  773. horizontal: "90deg",
  774. vertical: "180deg",
  775. "left-diagonal": "135deg",
  776. "right-diagonal": "45deg",
  777. };
  778. const deg = directionMap[propValue.bgDirection] || "180deg";
  779. style.background = `linear-gradient(${deg}, ${propValue.backgroundColor || "transparent"}, ${
  780. propValue.bgColor2
  781. })`;
  782. } else {
  783. style.backgroundColor = propValue.backgroundColor || "transparent";
  784. }
  785. if (propValue.showBorder && propValue.borderWidth) {
  786. style.backgroundSize = `100% calc(100% + ${propValue.borderWidth * scale * 4 || 0}rpx)`;
  787. style.backgroundPosition = `0rpx -${propValue.borderWidth * scale * 2 || 0}rpx`;
  788. }
  789. // Border Radius
  790. if (propValue.isRadiusAll) {
  791. style.borderRadius = (propValue.borderRadius || 0) * scale + "px";
  792. } else {
  793. style.borderRadius = `${(propValue.borderRadiusTopLeft || 0) * scale}px ${
  794. (propValue.borderRadiusTopRight || 0) * scale
  795. }px ${(propValue.borderRadiusBottomRight || 0) * scale}px ${(propValue.borderRadiusBottomLeft || 0) * scale}px`;
  796. }
  797. return style;
  798. },
  799. getLineStyle(propValue) {
  800. const style = {};
  801. if (propValue.direction === "vertical") {
  802. style.width = "0rpx";
  803. style.height = "100%";
  804. style.borderLeft = `${propValue.height * 2}rpx ${propValue.style} ${
  805. propValue.color
  806. }`;
  807. } else {
  808. style.width = "100%";
  809. style.height = "0rpx";
  810. style.borderTop = `${propValue.height * 2}rpx ${propValue.style} ${
  811. propValue.color
  812. }`;
  813. }
  814. return style;
  815. },
  816. getPanelStyle(propValue) {
  817. const scale = this.contentScale;
  818. let borderRadius = 0;
  819. if (propValue.isRadiusAll) {
  820. borderRadius = propValue.borderRadius * 2 + "rpx";
  821. } else {
  822. borderRadius = `${propValue.borderRadiusTopLeft * 2}rpx ${
  823. propValue.borderRadiusTopRight * 2
  824. }rpx ${propValue.borderRadiusBottomRight * 2}rpx ${
  825. propValue.borderRadiusBottomLeft * 2
  826. }rpx`;
  827. }
  828. const style = {
  829. width: "100%",
  830. height: "100%",
  831. borderRadius: borderRadius,
  832. borderWidth:
  833. (propValue.showBorder ? propValue.borderWidth * scale : 0) + "px",
  834. borderColor: propValue.borderColor || "transparent",
  835. borderStyle: propValue.borderStyle || "solid",
  836. };
  837. if (propValue.bgColor2) {
  838. const directionMap = {
  839. horizontal: "90deg",
  840. vertical: "180deg",
  841. "left-diagonal": "135deg",
  842. "right-diagonal": "45deg",
  843. };
  844. const deg = directionMap[propValue.bgDirection] || "180deg";
  845. style.backgroundImage = `linear-gradient(${deg}, ${
  846. propValue.backgroundColor || "transparent"
  847. }, ${propValue.bgColor2})`;
  848. } else {
  849. style.backgroundColor = propValue.backgroundColor || "transparent";
  850. }
  851. if (propValue.showBorder && propValue.borderWidth) {
  852. style.backgroundSize = `100% calc(100% + ${propValue.borderWidth * scale * 4 || 0}rpx)`;
  853. style.backgroundPosition = `0rpx -${propValue.borderWidth * scale * 2 || 0}rpx`;
  854. }
  855. if (propValue.showShadow) {
  856. style.boxShadow = `${(propValue.shadowX || 0) * scale * 2}rpx ${
  857. (propValue.shadowY || 0) * scale * 2
  858. }rpx ${(propValue.shadowBlur || 0) * scale * 2}rpx ${(propValue.shadowSpread || 0) * scale * 2}rpx ${
  859. propValue.shadowColor || "#000000"
  860. }`;
  861. }
  862. if (propValue.showBorder) {
  863. style.border = `${propValue.borderWidth * 2}rpx ${
  864. propValue.borderStyle
  865. } ${propValue.borderColor}`;
  866. }
  867. return style;
  868. },
  869. getPictureUrl(item, dataItem) {
  870. const field = item.propValue.fieldType;
  871. if (this.selectTypeValue === "user" && field === "image") {
  872. if (!this.isLogin) return item.propValue.url || "/static/images/f.png";
  873. return (
  874. this.userInfo.image || item.propValue.url || "/static/images/f.png"
  875. );
  876. }
  877. if (dataItem && field) {
  878. if (field === "image" && dataItem.image_input)
  879. return dataItem.image_input[0] || item.propValue.url;
  880. if (field === "image" && dataItem.image)
  881. return dataItem.image || item.propValue.url;
  882. }
  883. return item.propValue.url;
  884. },
  885. getDisplayText(item, dataItem) {
  886. const field = item.propValue.fieldType;
  887. if (this.selectTypeValue === "article") {
  888. // Article mapping
  889. if (field === "title") return dataItem.title;
  890. if (field === "visit") return dataItem.visit;
  891. if (field === "add_time") return dataItem.add_time;
  892. if (field === "synopsis") return dataItem.synopsis;
  893. } else if (this.selectTypeValue === "coupon") {
  894. // Coupon mapping
  895. const field = item.propValue.fieldType;
  896. if (field === "coupon_title")
  897. return dataItem.coupon_title || dataItem.title;
  898. if (field === "coupon_price") return dataItem.coupon_price;
  899. if (field === "use_min_price") return dataItem.use_min_price;
  900. if (field === "coupon_time") return dataItem.coupon_time;
  901. if (field === "type")
  902. return dataItem.type === 1
  903. ? "品类券"
  904. : dataItem.type === 2
  905. ? "商品券"
  906. : "通用券";
  907. if (field === "status") return dataItem.status === 1 ? "开启" : "关闭";
  908. if (field === "receive_time") return dataItem.receive_time;
  909. if (field === "use_time") return dataItem.use_time;
  910. if (field === "receive_count") return dataItem.receive_count;
  911. if (field === "add_time") return dataItem.add_time;
  912. } else if (this.selectTypeValue === "goods") {
  913. // Goods mapping
  914. if (field === "store_name") return dataItem.store_name;
  915. if (field === "id") return dataItem.id;
  916. if (field === "image") return dataItem.image;
  917. if (field === "store_info") return dataItem.store_info;
  918. if (field === "unit_name") return dataItem.unit_name;
  919. if (field === "cate_name") return dataItem.cate_name;
  920. if (field === "label_name") return dataItem.label_name;
  921. if (field === "stock") return dataItem.stock;
  922. if (field === "price") return dataItem.price;
  923. if (field === "max_price") return dataItem.max_price;
  924. if (field === "min_price") return dataItem.min_price;
  925. if (field === "ot_price") return dataItem.ot_price;
  926. if (field === "max_ot_price") return dataItem.max_ot_price;
  927. if (field === "min_ot_price") return dataItem.min_ot_price;
  928. if (field === "min_qty") return dataItem.min_qty;
  929. if (field === "sales") return dataItem.sales;
  930. if (field === "browse") return dataItem.browse;
  931. if (field === "add_time") return dataItem.add_time;
  932. } else if (this.selectTypeValue === "user") {
  933. // User mapping
  934. if (field === "nickname") return dataItem.nickname;
  935. if (field === "uid") return dataItem.uid;
  936. if (field === "image") return dataItem.avatar;
  937. if (field === "collection_num") return dataItem.collection_num;
  938. if (field === "cart_num") return dataItem.cart_num;
  939. if (field === "order_num") return dataItem.order_num;
  940. if (field === "integral") return dataItem.integral;
  941. if (field === "now_money") return dataItem.now_money;
  942. if (field === "brokerage_price") return dataItem.brokerage_price;
  943. if (field === "unread_msg_num") return dataItem.unread_msg_num;
  944. if (field === "mobile") return dataItem.mobile;
  945. if (field === "add_time") return dataItem.add_time;
  946. }
  947. return item.propValue.text;
  948. },
  949. goDetail(dataItem, item) {
  950. if (item.propValue.linkType === "detail") {
  951. if (this.selectTypeValue === "goods") {
  952. uni.navigateTo({
  953. url: "/pages/goods_details/index?id=" + dataItem.id,
  954. });
  955. } else if (this.selectTypeValue === "coupon") {
  956. this.receiveCoupon(dataItem);
  957. } else if (this.selectTypeValue === "article") {
  958. uni.navigateTo({
  959. url: "/pages/extension/news_details/index?id=" + dataItem.id,
  960. });
  961. }
  962. } else {
  963. uni.navigateTo({
  964. url: item.propValue.link,
  965. fail: () => {
  966. uni.switchTab({ url: item.propValue.link });
  967. },
  968. });
  969. }
  970. },
  971. receiveCoupon(item) {
  972. if (!this.isLogin) {
  973. this.$emit("changeLogin");
  974. return;
  975. }
  976. setCouponReceive(item.id)
  977. .then(() => {
  978. uni.showToast({ title: "领取成功", icon: "success" });
  979. })
  980. .catch((err) => {
  981. uni.showToast({ title: err, icon: "none" });
  982. });
  983. },
  984. handleUserClick(item) {
  985. if (item.propValue.link) {
  986. if (item.propValue.linkType === "url") {
  987. uni.navigateTo({
  988. url: item.propValue.link,
  989. });
  990. }
  991. } else if (!this.isLogin) {
  992. this.$emit("changeLogin"); // Trigger login modal via parent
  993. }
  994. },
  995. },
  996. };
  997. </script>
  998. <style scoped>
  999. .home_custom_component {
  1000. position: relative;
  1001. }
  1002. .component-wrapper {
  1003. position: relative;
  1004. }
  1005. .flex-center {
  1006. display: flex;
  1007. align-items: center;
  1008. justify-content: center;
  1009. }
  1010. .ellipsis-text {
  1011. /* #ifdef MP */
  1012. display: -webkit-box;
  1013. -webkit-box-orient: vertical;
  1014. /* #endif */
  1015. /* #ifndef MP */
  1016. display: -webkit-box;
  1017. -webkit-box-orient: vertical;
  1018. /* #endif */
  1019. overflow: hidden;
  1020. white-space: normal;
  1021. word-break: break-all;
  1022. }
  1023. </style>