Merge branch 'dev/1.0' into feat/camcard
This commit is contained in:
@@ -203,7 +203,8 @@ export const PAGE_KEY_MAP = {
|
||||
// === 名片云(card 插件) ===
|
||||
PAGE_CARD_LIST: '/pages_tool/card/list',
|
||||
PAGE_CARD_LEADS: '/pages_tool/card/leads',
|
||||
PAGE_CARD_CONTACTS: '/pages_tool/card/contacts',
|
||||
PAGE_CARD_CONTACTS: '/pages_tool/card/contacts',
|
||||
PAGE_CARD_ENTERPRISE: '/pages_tool/card/enterprise',
|
||||
|
||||
// === 名片云 · 商家端(后台接口就绪一部分, 先上有接口的页面) ===
|
||||
PAGE_CARD_ADMIN_MANAGE: '/pages_tool/card/admin/manage',
|
||||
|
||||
118
components-diy/diy-card-entry.vue
Normal file
118
components-diy/diy-card-entry.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<view data-component-name="diy-card-entry" class="diy-card-entry" :style="wrapStyle" @tap="goLink">
|
||||
<view class="entry-left">
|
||||
<text class="entry-title" :style="textStyle">{{ value.title || '扫名片 · 谈合作' }}</text>
|
||||
<text v-if="value.subtitle" class="entry-sub" :style="subStyle">{{ value.subtitle }}</text>
|
||||
</view>
|
||||
<view v-if="value.btnText" class="entry-btn" :style="btnStyle">
|
||||
<text class="entry-btn-text">{{ value.btnText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 数字名片 · 名片入口横幅(装修组件, 原生渲染不依赖 webview)
|
||||
import DiyMinx from './minx.js';
|
||||
import golbalConfig from '@/common/js/golbalConfig.js';
|
||||
|
||||
export default {
|
||||
name: 'diy-card-entry',
|
||||
props: {
|
||||
value: { type: Object }
|
||||
},
|
||||
mixins: [DiyMinx, golbalConfig],
|
||||
computed: {
|
||||
wrapStyle() {
|
||||
const v = this.value || {};
|
||||
return this.$util.objToStyle({
|
||||
background: 'linear-gradient(135deg, ' + (v.bgColorStart || '#2F6BFF') + ' 0%, ' + (v.bgColorEnd || '#1E40AF') + ' 100%)',
|
||||
borderRadius: this.px2rpx(v.borderRadius || 12) + 'rpx',
|
||||
padding: this.px2rpx(v.padding || 16) + 'rpx ' + this.px2rpx(v.padding || 16) + 'rpx'
|
||||
});
|
||||
},
|
||||
textStyle() {
|
||||
return this.$util.objToStyle({ color: (this.value || {}).textColor || '#FFFFFF' });
|
||||
},
|
||||
subStyle() {
|
||||
return this.$util.objToStyle({ color: (this.value || {}).textColor || '#FFFFFF', opacity: 0.85 });
|
||||
},
|
||||
btnStyle() {
|
||||
return this.$util.objToStyle({ background: (this.value || {}).accentColor || '#FFEB3B' });
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
px2rpx(v) {
|
||||
const n = Number(v);
|
||||
return isNaN(n) ? 0 : Math.round(n * 2);
|
||||
},
|
||||
goLink() {
|
||||
const v = this.value || {};
|
||||
let url = '';
|
||||
if (v.linkMode === 'contacts') {
|
||||
url = '/pages_tool/card/contacts';
|
||||
} else if (v.linkMode === 'custom') {
|
||||
url = String(v.customUrl || '').trim();
|
||||
if (url !== '' && url.indexOf('/') !== 0) {
|
||||
uni.showToast({ title: '自定义路径需以 / 开头', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
url = '/pages_tool/card/list';
|
||||
}
|
||||
if (url === '') {
|
||||
uni.showToast({ title: '未配置跳转路径', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
this.$util.redirectTo(url);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.diy-card-entry {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
overflow: hidden;
|
||||
|
||||
.entry-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.entry-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.entry-sub {
|
||||
font-size: 24rpx;
|
||||
line-height: 1.5;
|
||||
margin-top: 8rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.entry-btn {
|
||||
flex-shrink: 0;
|
||||
margin-left: 20rpx;
|
||||
padding: 12rpx 28rpx;
|
||||
border-radius: 999rpx;
|
||||
|
||||
.entry-btn-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
230
components-diy/diy-card-list.vue
Normal file
230
components-diy/diy-card-list.vue
Normal file
@@ -0,0 +1,230 @@
|
||||
<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>
|
||||
335
components-diy/diy-card-self.vue
Normal file
335
components-diy/diy-card-self.vue
Normal file
@@ -0,0 +1,335 @@
|
||||
<template>
|
||||
<view data-component-name="diy-card-self" class="diy-card-self">
|
||||
<!-- 未登录 + 登录引导兜底 -->
|
||||
<view v-if="!hasToken && fallbackMode === 'none'" class="self-login" :style="boxStyle" @tap="goLogin">
|
||||
<text class="login-tip" :style="nameStyle">{{ value.loginTip || '登录后这里展示我的名片' }}</text>
|
||||
<view class="login-btn" :style="detailStyle">
|
||||
<text class="login-btn-text">{{ value.loginBtnText || '去登录' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 名片卡(已登录=本人名片 / 未登录=兜底名片) -->
|
||||
<view v-else-if="card" class="self-box" :style="boxStyle">
|
||||
<view v-if="isSelf" class="self-badge" :style="badgeStyle">
|
||||
<text class="badge-text">我的名片</text>
|
||||
</view>
|
||||
|
||||
<view class="card-head">
|
||||
<view class="head-avatar" :style="avatarStyle">
|
||||
<image v-if="card.avatar" class="avatar-img" :src="$util.img(card.avatar)" mode="aspectFill" />
|
||||
<text v-else class="avatar-initial">{{ (card.name || '名').substring(0, 1) }}</text>
|
||||
</view>
|
||||
<view class="head-info">
|
||||
<text class="head-name" :style="nameStyle">{{ card.name || '未命名名片' }}</text>
|
||||
<text v-if="subLine" class="head-sub" :style="infoStyle">{{ subLine }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="showQuick && card.acts && card.acts.length" class="card-acts">
|
||||
<view v-for="(act, index) in card.acts" :key="index" class="act-btn" :style="actStyle" @tap.stop="doAct(act)">
|
||||
<text class="act-text" :style="actTextStyle">{{ act.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="showDetailBtn" class="detail-btn" :style="detailStyle" @tap.stop="goDetail">
|
||||
<text class="detail-text">{{ value.detailBtnText || '查看完整名片' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 已登录但没有任何名片可显示 -->
|
||||
<view v-else class="self-empty" :style="boxStyle">
|
||||
<text class="empty-tip" :style="infoStyle">登录后即可展示我的名片</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 数字名片 · 我的名片(装修组件)
|
||||
// 运行时判定: 操作者已登录 → 本人名片(optionalAuth); 未登录 → 配置的兜底名片
|
||||
// fallbackMode: enterprise=店铺企业名片 / card=指定 fallbackCardId / none=登录引导
|
||||
import DiyMinx from './minx.js';
|
||||
import golbalConfig from '@/common/js/golbalConfig.js';
|
||||
|
||||
export default {
|
||||
name: 'diy-card-self',
|
||||
props: {
|
||||
value: { type: Object }
|
||||
},
|
||||
mixins: [DiyMinx, golbalConfig],
|
||||
data() {
|
||||
return {
|
||||
card: null,
|
||||
isSelf: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasToken() {
|
||||
try {
|
||||
return !!(this.$store && this.$store.state && this.$store.state.token);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
fallbackMode() {
|
||||
const m = String((this.value || {}).fallbackMode || 'enterprise');
|
||||
return ['enterprise', 'card', 'none'].includes(m) ? m : 'enterprise';
|
||||
},
|
||||
showQuick() {
|
||||
const flag = this.getDiyToggle(['showQuick']);
|
||||
return flag === undefined ? true : flag;
|
||||
},
|
||||
showDetailBtn() {
|
||||
const flag = this.getDiyToggle(['showDetailBtn']);
|
||||
return flag === undefined ? true : flag;
|
||||
},
|
||||
subLine() {
|
||||
const c = this.card || {};
|
||||
return [c.job_title, c.company].filter(Boolean).join(' · ');
|
||||
},
|
||||
boxStyle() {
|
||||
const v = this.value || {};
|
||||
return this.$util.objToStyle({
|
||||
background: v.bgColor || '#FFFFFF',
|
||||
borderRadius: this.px2rpx(v.borderRadius || 16) + 'rpx',
|
||||
padding: this.px2rpx(v.padding || 16) + 'rpx'
|
||||
});
|
||||
},
|
||||
avatarStyle() {
|
||||
return this.$util.objToStyle({ background: (this.card || {}).theme_color || '#2F6BFF' });
|
||||
},
|
||||
nameStyle() {
|
||||
return this.$util.objToStyle({ color: (this.value || {}).nameColor || '#1F2329' });
|
||||
},
|
||||
infoStyle() {
|
||||
return this.$util.objToStyle({ color: (this.value || {}).infoColor || '#888888' });
|
||||
},
|
||||
actStyle() {
|
||||
const theme = (this.card || {}).theme_color || '#2F6BFF';
|
||||
return this.$util.objToStyle({ borderColor: theme });
|
||||
},
|
||||
actTextStyle() {
|
||||
return this.$util.objToStyle({ color: (this.card || {}).theme_color || '#2F6BFF' });
|
||||
},
|
||||
detailStyle() {
|
||||
return this.$util.objToStyle({ background: (this.card || {}).theme_color || '#2F6BFF' });
|
||||
},
|
||||
badgeStyle() {
|
||||
return this.$util.objToStyle({ background: (this.card || {}).theme_color || '#2F6BFF' });
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
hasToken() {
|
||||
this.fetchData();
|
||||
},
|
||||
'value.fallbackMode'() {
|
||||
this.fetchData();
|
||||
},
|
||||
'value.fallbackCardId'() {
|
||||
this.fetchData();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchData();
|
||||
},
|
||||
methods: {
|
||||
px2rpx(v) {
|
||||
const n = Number(v);
|
||||
return isNaN(n) ? 0 : Math.round(n * 2);
|
||||
},
|
||||
fetchData() {
|
||||
if (!this.hasToken && this.fallbackMode === 'none') {
|
||||
this.card = null;
|
||||
return;
|
||||
}
|
||||
this.$api.sendRequest({
|
||||
url: '/card/api/diy/mycard',
|
||||
data: {
|
||||
fallback_mode: this.fallbackMode,
|
||||
fallback_card_id: String((this.value || {}).fallbackCardId || '').trim()
|
||||
},
|
||||
success: res => {
|
||||
if (res.code == 0 && res.data) {
|
||||
this.card = res.data.card || null;
|
||||
this.isSelf = res.data.is_self == 1;
|
||||
} else {
|
||||
this.card = null;
|
||||
this.isSelf = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
goLogin() {
|
||||
this.$util.redirectTo('/pages_tool/login/login');
|
||||
},
|
||||
doAct(act) {
|
||||
if (!act || !act.value) return;
|
||||
if (act.type === 'tel') {
|
||||
uni.makePhoneCall({ phoneNumber: act.value, fail: () => {} });
|
||||
} else if (act.type === 'wx') {
|
||||
uni.setClipboardData({
|
||||
data: act.value,
|
||||
success: () => uni.showToast({ title: '微信号已复制', icon: 'none' })
|
||||
});
|
||||
} else if (act.type === 'mail') {
|
||||
// #ifdef H5
|
||||
window.location.href = 'mailto:' + act.value;
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.setClipboardData({
|
||||
data: act.value,
|
||||
success: () => uni.showToast({ title: '邮箱已复制', icon: 'none' })
|
||||
});
|
||||
// #endif
|
||||
} else {
|
||||
uni.setClipboardData({
|
||||
data: act.value,
|
||||
success: () => uni.showToast({ title: '已复制', icon: 'none' })
|
||||
});
|
||||
}
|
||||
},
|
||||
goDetail() {
|
||||
if (!this.card || !this.card.card_id) return;
|
||||
this.$util.redirectTo('/pages_tool/card/detail', { card_id: this.card.card_id });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.diy-card-self {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.self-box {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.self-badge {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border-radius: 0 16rpx 0 16rpx;
|
||||
padding: 6rpx 20rpx;
|
||||
|
||||
.badge-text {
|
||||
font-size: 20rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.head-avatar {
|
||||
flex-shrink: 0;
|
||||
width: 104rpx;
|
||||
height: 104rpx;
|
||||
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: 44rpx;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.head-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-left: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.head-name {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.head-sub {
|
||||
font-size: 24rpx;
|
||||
margin-top: 6rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-acts {
|
||||
margin-top: 24rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.act-btn {
|
||||
border: 2rpx solid;
|
||||
border-radius: 999rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
margin: 0 16rpx 16rpx 0;
|
||||
|
||||
.act-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
margin-top: 12rpx;
|
||||
border-radius: 999rpx;
|
||||
padding: 16rpx 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.detail-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.self-login,
|
||||
.self-empty {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.login-tip,
|
||||
.empty-tip {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
margin-top: 24rpx;
|
||||
border-radius: 999rpx;
|
||||
padding: 14rpx 48rpx;
|
||||
|
||||
.login-btn-text {
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
301
components-diy/diy-card-show.vue
Normal file
301
components-diy/diy-card-show.vue
Normal file
@@ -0,0 +1,301 @@
|
||||
<template>
|
||||
<view data-component-name="diy-card-show" class="diy-card-show">
|
||||
<view v-if="card" class="card-box" :style="boxStyle">
|
||||
<view class="card-head">
|
||||
<view class="head-avatar" :style="avatarStyle">
|
||||
<image v-if="card.avatar" class="avatar-img" :src="$util.img(card.avatar)" mode="aspectFill" />
|
||||
<text v-else class="avatar-initial">{{ (card.name || '名').substring(0, 1) }}</text>
|
||||
</view>
|
||||
<view class="head-info">
|
||||
<text class="head-name" :style="nameStyle">{{ card.name || '未命名名片' }}</text>
|
||||
<text v-if="subLine" class="head-sub" :style="infoStyle">{{ subLine }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="showRows && card.rows && card.rows.length" class="card-rows">
|
||||
<view v-for="(row, index) in card.rows" :key="index" class="row-line">
|
||||
<text class="row-label" :style="labelStyle">{{ row.label }}</text>
|
||||
<text class="row-value" :style="infoStyle">{{ row.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="showQuick && card.acts && card.acts.length" class="card-acts">
|
||||
<view v-for="(act, index) in card.acts" :key="index" class="act-btn" :style="actStyle" @tap.stop="doAct(act)">
|
||||
<text class="act-text" :style="actTextStyle">{{ act.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="showDetailBtn" class="detail-btn" :style="detailStyle" @tap.stop="goDetail">
|
||||
<text class="detail-text">{{ value.detailBtnText || '查看完整名片' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="card-empty" :style="boxStyle">
|
||||
<text class="empty-tip" :style="infoStyle">{{ emptyTip }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 数字名片 · 精选名片(装修组件): 单张名片的原生迷你卡
|
||||
// 数据口径: /card/api/diy/card?card_id=.. 公开摘要(与 wap 公开页同源)
|
||||
import DiyMinx from './minx.js';
|
||||
import golbalConfig from '@/common/js/golbalConfig.js';
|
||||
|
||||
export default {
|
||||
name: 'diy-card-show',
|
||||
props: {
|
||||
value: { type: Object }
|
||||
},
|
||||
mixins: [DiyMinx, golbalConfig],
|
||||
data() {
|
||||
return {
|
||||
card: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showRows() {
|
||||
const flag = this.getDiyToggle(['showRows']);
|
||||
return flag === undefined ? true : flag;
|
||||
},
|
||||
showQuick() {
|
||||
const flag = this.getDiyToggle(['showQuick']);
|
||||
return flag === undefined ? true : flag;
|
||||
},
|
||||
showDetailBtn() {
|
||||
const flag = this.getDiyToggle(['showDetailBtn']);
|
||||
return flag === undefined ? true : flag;
|
||||
},
|
||||
emptyTip() {
|
||||
return String((this.value || {}).cardId || '').trim() === '' ? '商家还未选择名片' : '名片不存在或已删除';
|
||||
},
|
||||
subLine() {
|
||||
const c = this.card || {};
|
||||
return [c.job_title, c.company].filter(Boolean).join(' · ');
|
||||
},
|
||||
boxStyle() {
|
||||
const v = this.value || {};
|
||||
return this.$util.objToStyle({
|
||||
background: v.bgColor || '#FFFFFF',
|
||||
borderRadius: this.px2rpx(v.borderRadius || 16) + 'rpx',
|
||||
padding: this.px2rpx(v.padding || 16) + 'rpx'
|
||||
});
|
||||
},
|
||||
avatarStyle() {
|
||||
return this.$util.objToStyle({ background: (this.card || {}).theme_color || '#2F6BFF' });
|
||||
},
|
||||
nameStyle() {
|
||||
return this.$util.objToStyle({ color: (this.value || {}).nameColor || '#1F2329' });
|
||||
},
|
||||
infoStyle() {
|
||||
return this.$util.objToStyle({ color: (this.value || {}).infoColor || '#888888' });
|
||||
},
|
||||
labelStyle() {
|
||||
return this.$util.objToStyle({ color: (this.value || {}).labelColor || '#999999' });
|
||||
},
|
||||
actStyle() {
|
||||
const theme = (this.card || {}).theme_color || '#2F6BFF';
|
||||
return this.$util.objToStyle({ borderColor: theme });
|
||||
},
|
||||
actTextStyle() {
|
||||
return this.$util.objToStyle({ color: (this.card || {}).theme_color || '#2F6BFF' });
|
||||
},
|
||||
detailStyle() {
|
||||
return this.$util.objToStyle({ background: (this.card || {}).theme_color || '#2F6BFF' });
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'value.cardId'() {
|
||||
this.fetchData();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchData();
|
||||
},
|
||||
methods: {
|
||||
px2rpx(v) {
|
||||
const n = Number(v);
|
||||
return isNaN(n) ? 0 : Math.round(n * 2);
|
||||
},
|
||||
fetchData() {
|
||||
const cardId = String((this.value || {}).cardId || '').trim();
|
||||
if (cardId === '') {
|
||||
this.card = null;
|
||||
return;
|
||||
}
|
||||
this.$api.sendRequest({
|
||||
url: '/card/api/diy/card',
|
||||
data: { card_id: cardId },
|
||||
success: res => {
|
||||
if (res.code == 0 && res.data && res.data.card) {
|
||||
this.card = res.data.card;
|
||||
} else {
|
||||
this.card = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
doAct(act) {
|
||||
if (!act || !act.value) return;
|
||||
if (act.type === 'tel') {
|
||||
uni.makePhoneCall({ phoneNumber: act.value, fail: () => {} });
|
||||
} else if (act.type === 'wx') {
|
||||
uni.setClipboardData({
|
||||
data: act.value,
|
||||
success: () => uni.showToast({ title: '微信号已复制', icon: 'none' })
|
||||
});
|
||||
} else if (act.type === 'mail') {
|
||||
// #ifdef H5
|
||||
window.location.href = 'mailto:' + act.value;
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.setClipboardData({
|
||||
data: act.value,
|
||||
success: () => uni.showToast({ title: '邮箱已复制', icon: 'none' })
|
||||
});
|
||||
// #endif
|
||||
} else {
|
||||
uni.setClipboardData({
|
||||
data: act.value,
|
||||
success: () => uni.showToast({ title: '已复制', icon: 'none' })
|
||||
});
|
||||
}
|
||||
},
|
||||
goDetail() {
|
||||
if (!this.card || !this.card.card_id) return;
|
||||
this.$util.redirectTo('/pages_tool/card/detail', { card_id: this.card.card_id });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.diy-card-show {
|
||||
width: 100%;
|
||||
|
||||
.card-box {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.head-avatar {
|
||||
flex-shrink: 0;
|
||||
width: 104rpx;
|
||||
height: 104rpx;
|
||||
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: 44rpx;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.head-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-left: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.head-name {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.head-sub {
|
||||
font-size: 24rpx;
|
||||
margin-top: 6rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-rows {
|
||||
margin-top: 24rpx;
|
||||
padding-top: 20rpx;
|
||||
border-top: 2rpx solid #f0f0f0;
|
||||
|
||||
.row-line {
|
||||
display: flex;
|
||||
line-height: 1.9;
|
||||
|
||||
.row-label {
|
||||
flex-shrink: 0;
|
||||
font-size: 24rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.row-value {
|
||||
flex: 1;
|
||||
font-size: 24rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-acts {
|
||||
margin-top: 24rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.act-btn {
|
||||
border: 2rpx solid;
|
||||
border-radius: 999rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
margin: 0 16rpx 16rpx 0;
|
||||
|
||||
.act-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-btn {
|
||||
margin-top: 12rpx;
|
||||
border-radius: 999rpx;
|
||||
padding: 16rpx 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.detail-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-empty {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.empty-tip {
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding: 30rpx 0;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -170,6 +170,22 @@
|
||||
<!-- 自定义推荐码组件 -->
|
||||
<diy-recommend-code :value="item"></diy-recommend-code>
|
||||
</template>
|
||||
<template v-else-if="item.componentName == 'DiyCardEntry'">
|
||||
<!-- 数字名片 · 名片入口横幅 -->
|
||||
<diy-card-entry :value="item"></diy-card-entry>
|
||||
</template>
|
||||
<template v-else-if="item.componentName == 'DiyCardList'">
|
||||
<!-- 数字名片 · 团队名片墙 -->
|
||||
<diy-card-list :value="item"></diy-card-list>
|
||||
</template>
|
||||
<template v-else-if="item.componentName == 'DiyCardShow'">
|
||||
<!-- 数字名片 · 精选名片 -->
|
||||
<diy-card-show :value="item"></diy-card-show>
|
||||
</template>
|
||||
<template v-else-if="item.componentName == 'DiyCardSelf'">
|
||||
<!-- 数字名片 · 我的名片(登录态自适应) -->
|
||||
<diy-card-self :value="item"></diy-card-self>
|
||||
</template>
|
||||
<template v-if="item.componentName == 'GoodsBrand'">
|
||||
<!-- 商品品牌 -->
|
||||
<diy-goods-brand :value="item"></diy-goods-brand>
|
||||
|
||||
Reference in New Issue
Block a user