469 lines
15 KiB
Vue
469 lines
15 KiB
Vue
<template>
|
||
<view data-component-name="diy-group" class="diy-group">
|
||
<view v-for="(item, index) in diyDataArray" :key="index" :style="item.pageStyle">
|
||
<view v-if="addonIsExist.store && item.componentName == 'StoreShow'">
|
||
<!-- 门店展示 -->
|
||
<diy-store :value="item"></diy-store>
|
||
</view>
|
||
|
||
<template v-if="item.componentName == 'Kefu'">
|
||
<!-- 客服按钮 -->
|
||
<diy-kefu :value="item"></diy-kefu>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Form'">
|
||
<!-- 表单组件 -->
|
||
<diy-form :value="item"></diy-form>
|
||
</template>
|
||
|
||
|
||
|
||
<template v-if="addonIsExist.store && item.componentName == 'StoreLabel'">
|
||
<!-- 门店标签 -->
|
||
<diy-store-label :value="item"></diy-store-label>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Picture'">
|
||
<!-- 单图组组件 -->
|
||
<diy-picture :value="item"></diy-picture>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Listmenu'">
|
||
<!-- 列表按钮组件 -->
|
||
<diy-listmenu :value="item"></diy-listmenu>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Text'">
|
||
<!-- 文本 -->
|
||
<diy-text :value="item"></diy-text>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Notice'">
|
||
<!-- 公告 -->
|
||
<diy-notice :value="item"></diy-notice>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'GraphicNav'">
|
||
<!-- 图文导航 -->
|
||
<diy-graphic-nav :value="item"></diy-graphic-nav>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'ImageAds'">
|
||
<!-- 图片广告 -->
|
||
<diy-img-ads :value="item"></diy-img-ads>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Search'">
|
||
<!-- 搜索 -->
|
||
<diy-search :value="item" :topNavColor="topNavColor" :global="diyGlobalData.global"
|
||
:haveTopCategory="haveTopCategory" :followOfficialAccount="followOfficialAccount"></diy-search>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'RichText'">
|
||
<!-- 富文本 -->
|
||
<diy-rich-text :value="item"></diy-rich-text>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'HorzLine'">
|
||
<!-- 辅助线 -->
|
||
<diy-horz-line :value="item"></diy-horz-line>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'HorzBlank'">
|
||
<!-- 辅助空白 -->
|
||
<diy-horz-blank :value="item"></diy-horz-blank>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Coupon' && addonIsExist.coupon">
|
||
<!-- 优惠券 -->
|
||
<diy-coupon :value="item"></diy-coupon>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'GoodsList'">
|
||
<!-- 商品列表 -->
|
||
<diy-goods-list :value="item"></diy-goods-list>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'ManyGoodsList'">
|
||
<!-- 多商品组 -->
|
||
<diy-many-goods-list :value="item" :global="diyGlobalData.global"
|
||
:scrollTop="scrollTop"></diy-many-goods-list>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'RubikCube'">
|
||
<!-- 魔方、橱窗 -->
|
||
<diy-rubik-cube :value="item"></diy-rubik-cube>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Video'">
|
||
<!-- 视频 -->
|
||
<diy-video :value="item"></diy-video>
|
||
</template>
|
||
|
||
|
||
|
||
|
||
<template v-if="item.componentName == 'Seckill' && addonIsExist.seckill">
|
||
<!-- 秒杀 -->
|
||
<diy-seckill :value="item"></diy-seckill>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Pintuan' && addonIsExist.pintuan">
|
||
<!-- 拼团 -->
|
||
<diy-pintuan :value="item"></diy-pintuan>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Groupbuy' && addonIsExist.groupbuy">
|
||
<!-- 团购 -->
|
||
<diy-groupbuy :value="item"></diy-groupbuy>
|
||
</template>
|
||
|
||
<!-- 拼团返利 -->
|
||
<template v-if="item.componentName == 'Pinfan' && addonIsExist.pinfan">
|
||
<diy-pinfan :value="item"></diy-pinfan>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Bargain' && addonIsExist.bargain">
|
||
<!-- 砍价 -->
|
||
<diy-bargain :value="item"></diy-bargain>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Presale' && addonIsExist.bargain">
|
||
<!-- 预售 -->
|
||
<diy-presale :value="item"></diy-presale>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Notes' && addonIsExist.notes">
|
||
<!-- 店铺笔记 -->
|
||
<diy-notes :value="item"></diy-notes>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'FloatBtn'">
|
||
<!-- 浮动按钮 -->
|
||
<diy-float-btn :value="item"></diy-float-btn>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'LiveInfo'">
|
||
<!-- 小程序直播 -->
|
||
<!-- #ifdef MP-WEIXIN -->
|
||
<diy-live :value="item"></diy-live>
|
||
<!-- #endif -->
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'FenxiaoGoodsList'">
|
||
<!-- 分销商品 -->
|
||
<diy-fenxiao-goods-list :value="item"></diy-fenxiao-goods-list>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'GoodsRecommend'">
|
||
<!-- 商品推荐 -->
|
||
<diy-goods-recommend :value="item"></diy-goods-recommend>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'GoodsBrand'">
|
||
<!-- 商品品牌 -->
|
||
<diy-goods-brand :value="item"></diy-goods-brand>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Article'">
|
||
<!-- 文章 -->
|
||
<diy-article :value="item"></diy-article>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'MerchList'">
|
||
<!-- 商户列表 -->
|
||
<diy-merch-list :value="item"></diy-merch-list>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'MemberInfo'">
|
||
<!-- 自定义会员中心——会员信息 -->
|
||
<diy-member-info ref="diyMemberIndex" :value="item" :global="diyGlobalData.global"></diy-member-info>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'MemberMyOrder'">
|
||
<!-- 自定义会员中心——我的订单 -->
|
||
<diy-member-my-order ref="diyMemberMyOrder" :value="item"></diy-member-my-order>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'QuickNav'">
|
||
<!-- 快捷导航 -->
|
||
<diy-quick-nav :value="item"></diy-quick-nav>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'PaymentQrcode'">
|
||
<!-- 付款码 -->
|
||
<diy-payment-qrcode :value="item"></diy-payment-qrcode>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'HotArea'">
|
||
<!-- 热区 -->
|
||
<diy-hot-area :value="item"></diy-hot-area>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'FollowOfficialAccount'">
|
||
<!-- 关注公众号 -->
|
||
<diy-follow-official-account :value="item"></diy-follow-official-account>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Map'">
|
||
<!-- 地图组件 -->
|
||
<diy-map :value="item"></diy-map>
|
||
</template>
|
||
|
||
|
||
<template v-if="item.componentName == 'Audio'">
|
||
<!-- 音频 -->
|
||
<diy-audio :value="item"></diy-audio>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'ImageNav'">
|
||
<!-- 图片导航 -->
|
||
<diy-image-nav :value="item"></diy-image-nav>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Digit'">
|
||
<!-- 数字 -->
|
||
<diy-digit :value="item"></diy-digit>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'VideoList'">
|
||
<!-- 视频列表 -->
|
||
<diy-video-list :value="item"></diy-video-list>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'BottomNav'">
|
||
<!-- 底部导航 -->
|
||
<diy-bottom-nav :value="item"></diy-bottom-nav>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'CategoryItem'">
|
||
<!-- 分类项 -->
|
||
<diy-category-item :value="item"></diy-category-item>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Category'">
|
||
<!-- 分类 -->
|
||
<diy-category :value="item"></diy-category>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Icon'">
|
||
<!-- 图标 -->
|
||
<diy-icon :value="item"></diy-icon>
|
||
</template>
|
||
|
||
<template v-if="item.componentName == 'Tab'">
|
||
<!-- Tab 组件 -->
|
||
<diy-tab :value="item" :diyGlobal="diyGlobalData"></diy-tab>
|
||
</template>
|
||
|
||
<template v-if="['ChannelList', 'WechatChannel'].includes(item.componentName)">
|
||
<!-- 视频号列表 -->
|
||
<diy-channel-list :value="item"></diy-channel-list>
|
||
</template>
|
||
|
||
<!-- ✅ 我的邀请码 / 推荐码(商家 DIY 自由放置:
|
||
1) 严格匹配:后端注册的 5 个常见名字
|
||
2) 宽松匹配:componentName 包含 Recommend / Invite 关键字(不区分大小写)—— 解决后端注册名和前端数组不一致导致的"加了组件但页面不显示"问题
|
||
后端只要把组件注册成任意含 Recommend 或 Invite 关键字的名字就能渲染出来,避免前端硬编码数组漏匹配 -->
|
||
<template v-if="(function(cn){
|
||
if (!cn) return false;
|
||
var s = String(cn).toLowerCase();
|
||
// 严格匹配
|
||
if (['MyInviteCode','RecommendCode','Recommend','InviteCode','RecommendCodeDiy'].indexOf(cn) !== -1) return true;
|
||
// 宽松匹配:含 recommend / invite 关键字
|
||
if (s.indexOf('recommend') !== -1 || s.indexOf('invite') !== -1) return true;
|
||
return false;
|
||
})(item.componentName)">
|
||
<components-recommend-diy :value="item"></components-recommend-diy>
|
||
</template>
|
||
|
||
<!-- 自定义扩展组件 -->
|
||
<diy-comp-extend :value="item"></diy-comp-extend>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// 组件组展示
|
||
import DiyMinx from './minx.js'
|
||
// ✅ 显式 import 我的邀请码 DIY 组件(同级文件,相对路径引入,不依赖 @ 别名)
|
||
import ComponentsRecommendDiy from './components-recommend-diy.vue'
|
||
|
||
export default {
|
||
name: 'diy-group',
|
||
components: {
|
||
// ✅ 显式注册:避免 easycom 没配置此目录时导致 "Unknown custom element"
|
||
ComponentsRecommendDiy
|
||
},
|
||
props: {
|
||
diyData: {
|
||
type: Object
|
||
},
|
||
scrollTop: {
|
||
type: [String, Number],
|
||
default: '0'
|
||
},
|
||
haveTopCategory: {
|
||
type: Boolean
|
||
},
|
||
followOfficialAccount: {
|
||
type: Object
|
||
},
|
||
},
|
||
mixins: [DiyMinx],
|
||
data() {
|
||
return {
|
||
diyGlobalData: this.diyData ? this.$util.deepClone(this.diyData) : null,
|
||
processedData: null
|
||
};
|
||
},
|
||
watch: {
|
||
diyData: {
|
||
handler() {
|
||
this.diyGlobalData = this.diyData ? this.$util.deepClone(this.diyData) : null;
|
||
this.processData();
|
||
this._notifyLoaded();
|
||
},
|
||
deep: true
|
||
}
|
||
},
|
||
created() {
|
||
console.log('[DIY-GROUP][created] hasDiyData=', !!this.diyData, 'valueLen=', this.diyData && this.diyData.value && this.diyData.value.length);
|
||
this.processData();
|
||
},
|
||
mounted() {
|
||
console.log('[DIY-GROUP][mounted] hasDiyData=', !!this.diyData, 'valueLen=', this.diyData && this.diyData.value && this.diyData.value.length);
|
||
if (this.diyData && this.diyData.value) {
|
||
this._notifyLoaded();
|
||
}
|
||
},
|
||
computed: {
|
||
topNavColor() {
|
||
if (!this.diyData?.global) return '';
|
||
|
||
const { topNavBg, topNavColor } = this.diyData.global;
|
||
|
||
if (topNavBg) {
|
||
return this.scrollTop > 20 ? topNavColor : 'transparent';
|
||
}
|
||
|
||
return topNavColor;
|
||
},
|
||
diyDataArray() {
|
||
if (!this.processedData) {
|
||
this.processData();
|
||
}
|
||
|
||
const showModuleData = this.$store.state.diyGroupShowModule
|
||
? JSON.parse(this.$store.state.diyGroupShowModule)
|
||
: null;
|
||
|
||
// ✅ 调试日志:showModuleData 白名单如果存在,会严格过滤,推荐码组件没加进白名单就会被过滤掉 → 控制台能直接看到
|
||
try {
|
||
if (showModuleData && showModuleData.length) {
|
||
console.log('[DIY-GROUP][diyDataArray] showModuleData 白名单 = ', JSON.stringify(showModuleData));
|
||
var hasRecommendInWhite = false;
|
||
showModuleData.forEach(function(n){
|
||
var s = String(n || '').toLowerCase();
|
||
if (s.indexOf('recommend') !== -1 || s.indexOf('invite') !== -1) hasRecommendInWhite = true;
|
||
});
|
||
if (!hasRecommendInWhite) {
|
||
console.warn('[DIY-GROUP][diyDataArray] ⚠️ 白名单 showModuleData 里没有推荐码/邀请码组件名,组件会被过滤掉不显示!请在后台 DIY 编辑器里的"显示模块"中勾选上该组件');
|
||
}
|
||
}
|
||
} catch (e) {}
|
||
|
||
if (!showModuleData || !showModuleData.length) {
|
||
return this.processedData || [];
|
||
}
|
||
|
||
if (showModuleData.includes('null')) {
|
||
return [];
|
||
}
|
||
|
||
// ✅ 关键修复:白名单过滤时,对推荐码组件也做宽松匹配(白名单里是 RecommendCode 但后端是 Recommend 也能过)
|
||
// 之前严格 includes(item.componentName) 会漏掉大小写/命名不一致的情况
|
||
var recKeys = ['myinvitecode','recommendcode','recommend','invitecode','recommendcodediy'];
|
||
return (this.processedData || []).filter(item => {
|
||
// 1) 严格命中白名单
|
||
if (showModuleData.includes(item.componentName)) return true;
|
||
// 2) 推荐码组件宽松命中:item 是推荐码类型 && 白名单里有任意推荐码关键字
|
||
var itemName = String(item.componentName || '').toLowerCase();
|
||
var isRecommendItem = (recKeys.indexOf(itemName) !== -1) || itemName.indexOf('recommend') !== -1 || itemName.indexOf('invite') !== -1;
|
||
if (isRecommendItem) {
|
||
var whiteHasRecommend = false;
|
||
showModuleData.forEach(function(n){
|
||
var s = String(n || '').toLowerCase();
|
||
if (recKeys.indexOf(s) !== -1 || s.indexOf('recommend') !== -1 || s.indexOf('invite') !== -1) whiteHasRecommend = true;
|
||
});
|
||
if (whiteHasRecommend) return true;
|
||
}
|
||
return false;
|
||
});
|
||
}
|
||
},
|
||
methods: {
|
||
_notifyLoaded() {
|
||
console.log('[DIY-GROUP][_notifyLoaded] enter, will emit loaded in 300ms');
|
||
if (this._loadedTimer) clearTimeout(this._loadedTimer);
|
||
this._loadedTimer = setTimeout(() => {
|
||
console.log('[DIY-GROUP][_notifyLoaded] emit loaded');
|
||
this.$emit('loaded');
|
||
}, 300);
|
||
},
|
||
processData() {
|
||
if (!this.diyGlobalData || !this.diyGlobalData.value) {
|
||
this.processedData = [];
|
||
return;
|
||
}
|
||
|
||
// ✅ 调试日志:后台保存组件后,用户在控制台能看到 ALL componentName 列表
|
||
// 方便定位:"后端加了组件但前端不显示" → 要么 componentName 没匹配,要么 showModuleData 过滤掉了
|
||
try {
|
||
const names = this.diyGlobalData.value.map(function(it){ return it && it.componentName ? it.componentName : '(no name)'; });
|
||
console.log('[DIY-GROUP][processData] 所有DIY组件名列表 = ', JSON.stringify(names));
|
||
// 进一步打印哪些是推荐码相关的
|
||
var matched = [];
|
||
var recommendKeys = ['myinvitecode','recommendcode','recommend','invitecode','recommendcodediy','recommend','invite'];
|
||
this.diyGlobalData.value.forEach(function(it, idx){
|
||
if (!it || !it.componentName) return;
|
||
var s = String(it.componentName).toLowerCase();
|
||
if (recommendKeys.indexOf(s) !== -1 || s.indexOf('recommend') !== -1 || s.indexOf('invite') !== -1) {
|
||
matched.push('idx=' + idx + ', name=' + it.componentName);
|
||
}
|
||
});
|
||
if (matched.length) console.log('[DIY-GROUP][processData] 命中推荐码/邀请码组件 = ', matched.join('; '));
|
||
} catch (e) {}
|
||
|
||
this.processedData = this.diyGlobalData.value.map((item, index) => {
|
||
const newItem = this.$util.deepClone(item);
|
||
|
||
newItem.pageStyle = '';
|
||
|
||
newItem.moduleIndex = this.diyData.tabIndex !== undefined
|
||
? `${this.diyData.tabIndex}-${index + 1}`
|
||
: index + 1;
|
||
|
||
if (newItem.componentName !== 'Search' || newItem.positionWay !== 'fixed') {
|
||
newItem.pageStyle = `background-color:${newItem.pageBgColor};`;
|
||
|
||
if (newItem.margin) {
|
||
newItem.pageStyle += `padding-top:${newItem.margin.top * 2}rpx;`;
|
||
newItem.pageStyle += `padding-bottom:${newItem.margin.bottom * 2}rpx;`;
|
||
newItem.pageStyle += `padding-right:${newItem.margin.both * 2}rpx;`;
|
||
newItem.pageStyle += `padding-left:${newItem.margin.both * 2}rpx;`;
|
||
}
|
||
}
|
||
|
||
return newItem;
|
||
});
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.diy-group {
|
||
width: 100%;
|
||
}
|
||
</style> |