- 新增 diy-card-entry/list/show/self.vue(easycom 自动加载): 头像/资料行/快捷操作原生渲染, tel 走 makePhoneCall, wx/mail/web 走复制, 点击卡片跳 pages_tool/card/detail - diy-card-self 登录态自适应: hasToken watch 自动刷新, 未登录兜底(企业卡/指定卡/登录引导)由后端 /card/api/diy/mycard 决定 - diy-group.vue: DiyCard* 四个 componentName 分发分支
231 lines
5.5 KiB
Vue
231 lines
5.5 KiB
Vue
<template>
|
||
<view data-component-name="diy-card-list" class="diy-card-list">
|
||
<view class="card-wall" :style="wallStyle">
|
||
<text v-if="value.title" class="wall-title" :style="titleStyle">{{ value.title }}</text>
|
||
|
||
<view v-if="list.length" class="wall-grid">
|
||
<view v-for="(item, index) in list" :key="index" class="wall-item" :style="itemStyle" @tap="goDetail(item)">
|
||
<view class="item-avatar" :style="avatarStyle(item)">
|
||
<image v-if="item.avatar" class="avatar-img" :src="$util.img(item.avatar)" mode="aspectFill" />
|
||
<text v-else class="avatar-initial" :style="initialStyle(item)">{{ (item.name || '名').substring(0, 1) }}</text>
|
||
</view>
|
||
<view class="item-info">
|
||
<text class="item-name" :style="nameStyle">{{ item.name || '未命名名片' }}</text>
|
||
<text v-if="infoLine(item)" class="item-meta" :style="infoStyle">{{ infoLine(item) }}</text>
|
||
</view>
|
||
<text v-if="showArrow" class="item-arrow" :style="infoStyle">›</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-else class="wall-empty" :style="infoStyle">
|
||
<text>{{ emptyTip }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// 数字名片 · 团队名片墙(装修组件)
|
||
// 数据口径: 商家在设计面板手动挑选 card_ids, 走 /card/api/diy/cards 公开摘要
|
||
import DiyMinx from './minx.js';
|
||
import golbalConfig from '@/common/js/golbalConfig.js';
|
||
|
||
export default {
|
||
name: 'diy-card-list',
|
||
props: {
|
||
value: { type: Object }
|
||
},
|
||
mixins: [DiyMinx, golbalConfig],
|
||
data() {
|
||
return {
|
||
list: []
|
||
};
|
||
},
|
||
computed: {
|
||
cardIds() {
|
||
return String((this.value || {}).cardIds || '')
|
||
.split(',')
|
||
.map(s => s.trim())
|
||
.filter(Boolean)
|
||
.slice(0, 20)
|
||
.join(',');
|
||
},
|
||
showArrow() {
|
||
const flag = this.getDiyToggle(['showArrow']);
|
||
return flag === undefined ? true : flag;
|
||
},
|
||
emptyTip() {
|
||
const v = this.value || {};
|
||
return String(v.cardIds || '').trim() === '' ? '商家还未挑选名片' : '暂无可展示的名片';
|
||
},
|
||
wallStyle() {
|
||
const v = this.value || {};
|
||
return this.$util.objToStyle({
|
||
background: v.bgColor || '#FFFFFF',
|
||
borderRadius: this.px2rpx(v.borderRadius || 12) + 'rpx',
|
||
padding: this.px2rpx(v.padding || 12) + 'rpx'
|
||
});
|
||
},
|
||
titleStyle() {
|
||
return this.$util.objToStyle({ color: (this.value || {}).titleColor || '#333333' });
|
||
},
|
||
nameStyle() {
|
||
return this.$util.objToStyle({ color: (this.value || {}).nameColor || '#1F2329' });
|
||
},
|
||
infoStyle() {
|
||
return this.$util.objToStyle({ color: (this.value || {}).infoColor || '#888888' });
|
||
},
|
||
itemStyle() {
|
||
const v = this.value || {};
|
||
const gap = Number(v.gap) || 10;
|
||
const cols = Number(v.columns) === 1 ? 1 : 2;
|
||
const width = cols === 1 ? '100%' : 'calc(50% - ' + this.px2rpx(gap / 2) + 'rpx)';
|
||
return this.$util.objToStyle({
|
||
width: width,
|
||
marginBottom: this.px2rpx(gap) + 'rpx'
|
||
});
|
||
}
|
||
},
|
||
watch: {
|
||
'value.cardIds'() {
|
||
this.fetchData();
|
||
}
|
||
},
|
||
mounted() {
|
||
this.fetchData();
|
||
},
|
||
methods: {
|
||
px2rpx(v) {
|
||
const n = Number(v);
|
||
return isNaN(n) ? 0 : Math.round(n * 2);
|
||
},
|
||
fetchData() {
|
||
if (this.cardIds === '') {
|
||
this.list = [];
|
||
return;
|
||
}
|
||
this.$api.sendRequest({
|
||
url: '/card/api/diy/cards',
|
||
data: { card_ids: this.cardIds },
|
||
success: res => {
|
||
if (res.code == 0 && res.data) {
|
||
this.list = res.data.list || [];
|
||
}
|
||
}
|
||
});
|
||
},
|
||
avatarStyle(item) {
|
||
return this.$util.objToStyle({ background: item.theme_color || '#2F6BFF' });
|
||
},
|
||
initialStyle(item) {
|
||
return this.$util.objToStyle({ color: '#FFFFFF' });
|
||
},
|
||
infoLine(item) {
|
||
const v = this.value || {};
|
||
const parts = [];
|
||
const showJob = this.getDiyToggle(['showJob']);
|
||
const showCompany = this.getDiyToggle(['showCompany']);
|
||
if ((showJob === undefined || showJob) && item.job_title) parts.push(item.job_title);
|
||
if ((showCompany === undefined || showCompany) && item.company) parts.push(item.company);
|
||
return parts.join(' · ');
|
||
},
|
||
goDetail(item) {
|
||
if (!item || !item.card_id) return;
|
||
this.$util.redirectTo('/pages_tool/card/detail', { card_id: item.card_id });
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.diy-card-list {
|
||
width: 100%;
|
||
|
||
.card-wall {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
|
||
.wall-title {
|
||
display: block;
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.wall-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.wall-item {
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
align-items: center;
|
||
border: 2rpx solid #f0f0f0;
|
||
border-radius: 16rpx;
|
||
padding: 20rpx;
|
||
overflow: hidden;
|
||
|
||
.item-avatar {
|
||
flex-shrink: 0;
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
border-radius: 50%;
|
||
overflow: hidden;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.avatar-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
}
|
||
|
||
.avatar-initial {
|
||
font-size: 36rpx;
|
||
font-weight: 700;
|
||
}
|
||
}
|
||
|
||
.item-info {
|
||
flex: 1;
|
||
min-width: 0;
|
||
margin-left: 16rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.item-name {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.item-meta {
|
||
font-size: 22rpx;
|
||
margin-top: 4rpx;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
|
||
.item-arrow {
|
||
flex-shrink: 0;
|
||
margin-left: 8rpx;
|
||
font-size: 32rpx;
|
||
}
|
||
}
|
||
|
||
.wall-empty {
|
||
padding: 30rpx 0;
|
||
font-size: 24rpx;
|
||
text-align: center;
|
||
}
|
||
}
|
||
}
|
||
</style>
|