Merge branch 'feat/camcard' into dev/1.0
This commit is contained in:
@@ -235,7 +235,9 @@ export default {
|
||||
designEnabled: true,
|
||||
// 旧入口显隐开关(2026-10-21 需求: 隐藏「编辑资料/名片样式/切换名片」「潜在客户/我的人脉」,
|
||||
// 隐藏非删除——改回 true 即全部恢复; 对应方法 goEdit/onStyleTap/goLeads/goContacts 均保留)
|
||||
showLegacyActions: false
|
||||
showLegacyActions: false,
|
||||
|
||||
loadOptions: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -369,11 +371,37 @@ export default {
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.cardId = decodeURIComponent(options.card_id || '');
|
||||
this.loadOptions =JSON.stringify(options);
|
||||
// ===== 调试转储(2026-10-27): 定位「缺少名片编号」——看本页到底收到了什么入参 =====
|
||||
// 小程序/扫码/转发进入时, 平台传给 onLoad 的所有参数都打出来(含 scene 解码后)
|
||||
try {
|
||||
console.log('[detail] onLoad 原始 options:', JSON.stringify(options || {}));
|
||||
Object.keys(options || {}).forEach(k => {
|
||||
let v = options[k];
|
||||
try { v = decodeURIComponent(v); } catch (e) {}
|
||||
console.log('[detail] options.' + k, '=', v);
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('[detail] options 转储失败:', e);
|
||||
}
|
||||
|
||||
// ===== 调试转储结束 =====
|
||||
this.applyPageTitle();
|
||||
this.loadDetail();
|
||||
this.loadExchange();
|
||||
this.loadDesignEnabled();
|
||||
// 2.21 小程序码入口: scene 里只有名片**主键 id**(微信 scene 仅 32 字符,
|
||||
// 装不下 30 字符的业务 card_id), 需先换成 card_id 才能查详情。
|
||||
const directId = this.pickDirectCardId(options);
|
||||
const sceneId = this.pickSceneRelId(options);
|
||||
if (directId) {
|
||||
this.cardId = directId;
|
||||
this.loadDetail();
|
||||
this.loadExchange();
|
||||
this.loadDesignEnabled();
|
||||
} else if (sceneId) {
|
||||
this.resolveCardIdByRelId(sceneId);
|
||||
} else {
|
||||
this.isLoading = false;
|
||||
this.errorMsg = `缺少名片编号 ${this.loadOptions}`;
|
||||
}
|
||||
},
|
||||
// H5 直接刷新/深链进入时, uni-page-head 可能在路由 meta 就绪前就挂载,
|
||||
// 此时标题会回落到 document.title(uni-app H5 默认值 "title");
|
||||
@@ -406,6 +434,77 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// 2.21 小程序码: 名片 ID 解析
|
||||
//
|
||||
// 微信 getwxacodeunlimit 的 scene 限制 32 可见字符, 装不下业务 card_id
|
||||
// (形如 c_1_shop_20261013111612_9588EA, 30 字符) → scene 里放的是
|
||||
// 名片**主键 id**(数字)。故两条入口:
|
||||
// ① 常规路由: query 带 card_id → 直接用
|
||||
// ② 小程序码: options.scene = "id=123" → 先按主键换业务 card_id
|
||||
//
|
||||
// 常规入口(①)
|
||||
pickDirectCardId(options) {
|
||||
const opt = options || {};
|
||||
if (opt.card_id) {
|
||||
try {
|
||||
return decodeURIComponent(opt.card_id);
|
||||
} catch (e) {
|
||||
return opt.card_id;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
// 小程序码入口(②): 从 scene 里取名片主键 id
|
||||
// scene 形态: "id=123" (微信会先 encodeURIComponent, 需先解码)
|
||||
pickSceneRelId(options) {
|
||||
const opt = options || {};
|
||||
if (!opt.scene) {
|
||||
return 0;
|
||||
}
|
||||
let scene = '';
|
||||
try {
|
||||
scene = decodeURIComponent(opt.scene);
|
||||
} catch (e) {
|
||||
scene = opt.scene;
|
||||
}
|
||||
// 兼容 "id=123" 与裸 "123"
|
||||
let val = scene;
|
||||
const pairs = scene.split('&');
|
||||
for (let i = 0; i < pairs.length; i++) {
|
||||
const kv = pairs[i].split('=');
|
||||
if (kv[0] === 'id' && kv[1]) {
|
||||
val = kv[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
val = String(val).replace(/[^0-9]/g, '');
|
||||
return val ? parseInt(val, 10) : 0;
|
||||
},
|
||||
// 按主键 id 换业务 card_id(后端 /card/api/diy/card 支持 id 参数寻址)
|
||||
resolveCardIdByRelId(relId) {
|
||||
this.isLoading = true;
|
||||
this.$cardApi.cardByRelId({
|
||||
data: { id: relId },
|
||||
success: res => {
|
||||
const card = (res.code == 0 && res.data && res.data.card) ? res.data.card : null;
|
||||
const bizId = (card && card.card_id) ? card.card_id : '';
|
||||
if (!bizId) {
|
||||
this.isLoading = false;
|
||||
this.errorMsg = '名片不存在或已被删除';
|
||||
this.relation = null;
|
||||
return;
|
||||
}
|
||||
this.cardId = bizId;
|
||||
this.loadDetail();
|
||||
this.loadExchange();
|
||||
this.loadDesignEnabled();
|
||||
},
|
||||
fail: () => {
|
||||
this.isLoading = false;
|
||||
this.errorMsg = '网络异常,请稍后重试';
|
||||
},
|
||||
});
|
||||
},
|
||||
// 导航栏标题统一出口: 小程序/鸿蒙/App 走原生导航栏;
|
||||
// H5 额外同步 document.title, 避免 uni-page-head 标题回落到默认值 "title"。
|
||||
applyPageTitle() {
|
||||
@@ -446,7 +545,7 @@ export default {
|
||||
loadDetail(retry) {
|
||||
if (!this.cardId) {
|
||||
this.isLoading = false;
|
||||
this.errorMsg = '缺少名片编号';
|
||||
this.errorMsg = `缺少名片编号 ${this.loadOptions}`;
|
||||
return;
|
||||
}
|
||||
this.isLoading = true;
|
||||
@@ -454,6 +553,13 @@ export default {
|
||||
data: { card_id: this.cardId },
|
||||
success: res => {
|
||||
const data = res.data || {};
|
||||
// ===== 调试转储(2026-10-27): 把 card/detail 后台数据打到控制台 =====
|
||||
// 完整响应可在控制台展开查看; 关键字段单独打出, 便于快速核对二维码/公开页地址
|
||||
console.log('[detail] card/detail 返回(code=' + res.code + '):', JSON.parse(JSON.stringify(res)));
|
||||
console.log('[detail] 关键字段 -> card_id:', this.cardId,
|
||||
'| wap_url:', data.wap_url || '(空)',
|
||||
'| qr_url:', data.qr_url || '(空)');
|
||||
// ===== 调试转储结束 =====
|
||||
if (res.code == 0) {
|
||||
this.isLoading = false;
|
||||
this.relation = data.relation || null;
|
||||
|
||||
@@ -9,8 +9,21 @@
|
||||
<image v-if="form.avatar" :src="$util.img(form.avatar)" class="avatar-img" mode="aspectFill" />
|
||||
<text v-else class="avatar-plus">+</text>
|
||||
</view>
|
||||
<view class="avatar-tip">点击从相册选择</view>
|
||||
<view class="avatar-tip">点击从相册选择</view>
|
||||
</view>
|
||||
<!-- 2.20 自定义二维码图片: 空=用系统按名片链接自动生成的码 -->
|
||||
<view class="form-row">
|
||||
<view class="row-label">二维码</view>
|
||||
<view class="qr-box" @tap="chooseQrImage">
|
||||
<image v-if="form.qr_image" :src="$util.img(form.qr_image)" class="qr-img" mode="aspectFit" />
|
||||
<text v-else class="qr-plus">+</text>
|
||||
</view>
|
||||
<view class="qr-side">
|
||||
<view class="avatar-tip">点击上传自定义二维码</view>
|
||||
<view class="qr-tip">{{ form.qr_image ? '已自定义,将替换默认二维码' : '未设置时使用名片链接自动生成的码' }}</view>
|
||||
<text v-if="form.qr_image" class="qr-clear" @tap.stop="clearQrImage">移除自定义二维码</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-row">
|
||||
<view class="row-label"><text class="req-star">*</text>姓名</view>
|
||||
<input class="row-input" :value="form.name" placeholder="请输入姓名"
|
||||
@@ -180,7 +193,9 @@ export default {
|
||||
name: '', company: '', job_title: '', avatar: '',
|
||||
mobile: '', phone: '', email: '', website: '', wechat: '',
|
||||
country: '', province: '', city: '', address: '', postal_code: '',
|
||||
bio: ''
|
||||
bio: '',
|
||||
// 2.20 自定义二维码图片(空=用系统自动生成的码)
|
||||
qr_image: ''
|
||||
},
|
||||
mobiles: [],
|
||||
socials: [],
|
||||
@@ -227,6 +242,12 @@ export default {
|
||||
this.form[k] = String(r[k]);
|
||||
}
|
||||
});
|
||||
// 2.20 自定义二维码: 优先取后端归一化后的 qr_image(顶层),
|
||||
// 回退 relation 原始值(归一化只影响站内相对路径补斜杠)
|
||||
const qrNorm = res.data && res.data.qr_image;
|
||||
this.form.qr_image = qrNorm !== undefined && qrNorm !== null
|
||||
? String(qrNorm)
|
||||
: String(r.qr_image || '');
|
||||
// 富化字段: detail 由后端 CardRichFields 解码后挂在 relation 上
|
||||
// (注意是 res.data.relation.mobiles_list, 不是 res.data.mobiles_list)
|
||||
this.mobiles = (r.mobiles_list || []).map(v => String(v || ''));
|
||||
@@ -284,6 +305,41 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/* ---------- 自定义二维码图片(2.20): 相册选择 → card 专用上传 → 回填路径 ---------- */
|
||||
chooseQrImage() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: res => {
|
||||
const filePath = res.tempFilePaths && res.tempFilePaths[0];
|
||||
if (!filePath) return;
|
||||
uni.showLoading({ title: '上传中…', mask: true });
|
||||
this.$api.upload({
|
||||
url: this.$cardApi.url.CARD_UPLOAD_QR,
|
||||
filePath: filePath,
|
||||
fileType: 'image',
|
||||
success: r => {
|
||||
uni.hideLoading();
|
||||
if (r && r.code == 0 && r.data && r.data.pic_path) {
|
||||
this.form.qr_image = r.data.pic_path;
|
||||
} else {
|
||||
uni.showToast({ title: (r && r.message) || '上传失败', icon: 'none' });
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '上传失败', icon: 'none' });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
/* 移除自定义二维码: 置空 = 提交时回落系统自动生成的码 */
|
||||
clearQrImage() {
|
||||
this.form.qr_image = '';
|
||||
},
|
||||
|
||||
/* ---------- 附加手机号 ---------- */
|
||||
onMobileInput(i, e) {
|
||||
this.$set(this.mobiles, i, e.detail.value);
|
||||
@@ -499,6 +555,49 @@ export default {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 自定义二维码行(2.20) */
|
||||
.qr-box {
|
||||
width: 108rpx;
|
||||
height: 108rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #f2f3f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
|
||||
.qr-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.qr-plus {
|
||||
font-size: 44rpx;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
|
||||
.qr-side {
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.qr-tip {
|
||||
font-size: 22rpx;
|
||||
color: #b0b4bc;
|
||||
margin-top: 4rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.qr-clear {
|
||||
display: inline-block;
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
color: var(--main-color, #0054a5);
|
||||
}
|
||||
|
||||
/* 动态行(附加手机号/社交媒体/自定义字段) */
|
||||
.dyn-row {
|
||||
.row-label {
|
||||
|
||||
Reference in New Issue
Block a user