From f1bd606e0bd2ae5653d91ff2fa0fc8d457e8c66f Mon Sep 17 00:00:00 2001 From: ZF sun <34314687@qq.com> Date: Wed, 21 Oct 2026 14:09:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(diy-card):=20=E5=AE=A1=E8=AE=A1=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E2=80=94=E2=80=94corner=20=E6=A1=A3=E5=A4=B4=E9=83=A8?= =?UTF-8?q?=E6=B0=B4=E5=B9=B3=E8=AE=A9=E4=BD=8D=E5=8A=A8=E6=80=81=E5=8C=96?= =?UTF-8?q?=20+=20self=20=E9=A6=96=E8=BD=BD=E9=AA=A8=E6=9E=B6=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 P1: corner 档自定义 qrSize>120 时静态 padding-right:180rpx 让位不足, 浮层压住头部姓名区 → 新增 cornerHeadStyle 按浮层实际 宽度(尺寸+衬底+右偏移20+呼吸20)动态反算, 与 cornerClearStyle 垂直让位同口径; 废弃静态 CSS 值(负断言锁定) - 修复: self.vue 已登录首次加载三分支都不渲染(loading 且无 card) → 白屏闪烁; 改 v-else-if="loading || card" 挂上 mini 骨架屏 - 契约测试: 新增 cornerHeadStyle 断言 + resolveQrSize 三处口径 + 静态让位废弃负断言; 3 项变异验证全抓住; 四文件 67 项全绿 --- components-diy/diy-card-mini.vue | 21 ++++++++++++++++----- components-diy/diy-card-self.vue | 4 ++-- tests/diy-card-ab-visual.test.js | 6 ++++-- tests/diy-card-qr-style.test.js | 14 ++++++++++++-- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/components-diy/diy-card-mini.vue b/components-diy/diy-card-mini.vue index 0381eff..05a0d64 100644 --- a/components-diy/diy-card-mini.vue +++ b/components-diy/diy-card-mini.vue @@ -45,7 +45,7 @@ {{ qrTip || '扫码查看完整名片' }} - + {{ initialOf(card.name) }} @@ -177,6 +177,17 @@ export default { showAvatarBlock() { return flagOn(this.c.showAvatar, true); }, + /** + * corner 档头部水平让位: 按浮层实际宽度反算(浮层宽[尺寸+衬底] + 右偏移20 + 呼吸20), + * 自定义大尺寸时自动跟随 —— 修复静态 180rpx 在 qrSize>120 时让位不足压住姓名区; + * 非 corner 档 / 二维码未启用时输出空对象(浮层不存在无需让位) + */ + cornerHeadStyle() { + if (this.qrPos !== 'corner' || !this.showQrBase) return {}; + const size = resolveQrSize(this.qrPos, this.c.qrSize); + const pad = this.qrStyle === 'plain' ? 0 : (this.qrStyle === 'gradient' ? 12 : 20); + return { paddingRight: (size + pad + 40) + 'rpx' }; + }, /** * corner 档资料行/操作区让位量: 按二维码实际占高反算 * (浮层 top 20 + 块高[尺寸+样式衬底+tip] - 头部 104 + 间隙 10), 防遮挡; @@ -587,12 +598,12 @@ export default { } } - /* corner 档: 头部右侧留出浮层宽度: WXSS 不支持 calc(), 用固定 padding-right - (浮层 120rpx + 右偏移 20rpx + 呼吸 20rpx ≈ 160, 取 180 富余); - 资料行/操作区让位改由 cornerClearStyle 按实际尺寸动态算 */ + /* corner 档: 头部右侧让位由 cornerHeadStyle 按浮层实际宽度动态给出 + (静态 180rpx 在自定义大尺寸 qrSize 下不足, 已废弃); WXSS 不支持 calc() */ &.qr-at-corner { .mini-head { - padding-right: 180rpx; + position: relative; + z-index: 2; } } diff --git a/components-diy/diy-card-self.vue b/components-diy/diy-card-self.vue index d4ec737..d0539e3 100644 --- a/components-diy/diy-card-self.vue +++ b/components-diy/diy-card-self.vue @@ -8,9 +8,9 @@ - + { test('mini: corner 档 Logo 避让 + 资料行/操作区动态让位', () => { assert.match(miniSrc, /showLogo\(\)\s*\{[\s\S]*?this\.qrPos !== 'corner'/); assert.match(miniSrc, /:class="\{ 'qr-at-corner': qrPos === 'corner' \}"/); - // 头部固定让位(头像|信息|二维码 三段 flex, 信息区不被右上浮层压) - assert.match(miniSrc, /&\.qr-at-corner \{[\s\S]*?\.mini-head\s*\{\s*padding-right: 180rpx;/); + // 头部水平让位 = cornerHeadStyle 动态反算(审计修复: 静态 180rpx 在自定义大尺寸下不足) + assert.match(miniSrc, //); // 资料行/操作区让位 = cornerClearStyle 动态反算(自定义大尺寸/关 tip 自动跟随, 不用 calc) assert.match(miniSrc, /:style="cornerClearStyle"/); + // 静态 CSS 让位必须已移除(动态值才是单一事实来源) + assert.ok(!/padding-right: 180rpx;/.test(miniSrc), '静态 180rpx 让位应废弃, 改由 cornerHeadStyle 动态给出'); // 浮层 CSS 只定位于层次; 视觉衬底(白卡等)由 qrBlockStyle 内联决定 assert.match(miniSrc, /&\.mini-qr-corner \{\s*position: absolute;\s*top: 20rpx;\s*right: 20rpx;\s*z-index: 3;\s*margin-top: 0;\s*box-shadow:/); assert.ok(!/&\.mini-qr-corner \{[\s\S]*?background: #ffffff;/.test(miniSrc), diff --git a/tests/diy-card-qr-style.test.js b/tests/diy-card-qr-style.test.js index aa123ef..2415f56 100644 --- a/tests/diy-card-qr-style.test.js +++ b/tests/diy-card-qr-style.test.js @@ -126,9 +126,10 @@ test('mini: qrBlockStyle 四档外观分支(多彩只装饰外围, 码黑白保 }); test('mini: 二维码尺寸取 resolveQrSize(自定义>0 优先, 否则档位自动)', () => { - // qrBlockStyle 与 cornerClearStyle 都按同一口径反算(自定义大尺寸时让位自动跟随) + // qrBlockStyle + cornerClearStyle(垂直让位) + cornerHeadStyle(水平让位) 三处同口径 + // (自定义大尺寸时浮层占高与占宽都要动态跟随) const hits = miniSrc.match(/resolveQrSize\(this\.qrPos, this\.c\.qrSize\)/g) || []; - assert.equal(hits.length, 2, 'qrBlockStyle + cornerClearStyle 两处, 实际 ' + hits.length); + assert.equal(hits.length, 3, 'qrBlockStyle + cornerClearStyle + cornerHeadStyle 三处, 实际 ' + hits.length); }); test('mini: 微调位移 transform(不用 calc, WXSS 兼容)', () => { @@ -152,6 +153,15 @@ test('mini: cornerClearStyle 按实际尺寸反算让位(防遮挡, 纯数值不 assert.match(miniSrc, /const tip = this\.showQrTip \? 42 : 0;/); }); +test('mini: cornerHeadStyle 头部水平让位按浮层实际宽度动态反算', () => { + // 审计修复: 静态 180rpx 在自定义 qrSize>120 时不足 → 头部让位动态化 + assert.match(miniSrc, /cornerHeadStyle\(\)\s*\{[\s\S]*?if \(this\.qrPos !== 'corner' \|\| !this\.showQrBase\) return \{\};/); + // 浮层宽[尺寸+衬底] + 右偏移 20 + 呼吸 20 + assert.match(miniSrc, /paddingRight: \(size \+ pad \+ 40\) \+ 'rpx'/); + // 模板头部绑定了动态让位(浮层不存在时输出空对象不占位) + assert.match(miniSrc, //); +}); + test('mini: 四个二维码块统一绑 qrBlockStyle(尺寸/位移/外观单一事实来源)', () => { const binds = miniSrc.match(/:style="qrBlockStyle"/g) || []; assert.equal(binds.length, 4, 'corner/head/rows/bottom 四块, 实际 ' + binds.length);