From 1b6114ef5885a696a1190b2d4ef0e1831593cde6 Mon Sep 17 00:00:00 2001 From: ZF sun <34314687@qq.com> Date: Sat, 24 Oct 2026 09:42:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(diy-card):=20=E9=BB=98=E8=AE=A4=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E5=88=A0=E9=99=A4+=E6=81=A2=E5=A4=8D(2.19.2)=20?= =?UTF-8?q?=E2=80=94=20normalizeActDeleted=20+=20acts=20=E5=8F=8C=E9=87=8D?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=20+=20=E9=80=8F=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components-diy/diy-card-common.js | 14 ++++++++++++++ components-diy/diy-card-mini.vue | 7 ++++--- components-diy/diy-card-self.vue | 1 + components-diy/diy-card-show.vue | 1 + tests/diy-card-style-v2.test.js | 31 ++++++++++++++++++++++--------- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/components-diy/diy-card-common.js b/components-diy/diy-card-common.js index aff9d5d..7bb89a2 100644 --- a/components-diy/diy-card-common.js +++ b/components-diy/diy-card-common.js @@ -396,6 +396,20 @@ export function normalizeActVis(vis) { return out; } +/** + * 默认动作删除(2.19.2): actDeleted = { type: 0|1 }, 1=已删除(C端不渲染, 面板行隐藏, + * 经「恢复」chips 找回)。与 actVis(眼睛临时隐藏)独立 —— 删除与隐藏 C 端均不渲染。 + * 缺键/非法 = 未删除(0), 历史配置无此字段 → 零变化 + */ +export function normalizeActDeleted(deleted) { + const src = deleted && typeof deleted === 'object' ? deleted : {}; + const out = {}; + ACT_TYPES.forEach(t => { + out[t] = String(src[t]) === '1' ? 1 : 0; + }); + return out; +} + /** * 默认动作文案(2.19): actLabels = { type: string }, 空/缺 = 内置默认 * 历史配置无此字段 → 全默认, 渲染零变化 diff --git a/components-diy/diy-card-mini.vue b/components-diy/diy-card-mini.vue index e0cb872..d714f49 100644 --- a/components-diy/diy-card-mini.vue +++ b/components-diy/diy-card-mini.vue @@ -192,7 +192,7 @@ import { WAP_LINE_ICONS, svgLineIconBg, ACT_GRID_PRESETS, ACT_GRID_PRESET_FALLBACK, hexToRgba, // 2.19: 显隐/文案/自定义动作归一化 + 素材图标库(与后端 CardIconLibrary 同源) - normalizeActVis, normalizeActLabels, normalizeCustomActs, normalizeActOrder, libIconBg, + normalizeActVis, normalizeActDeleted, normalizeActLabels, normalizeCustomActs, normalizeActOrder, libIconBg, } from './diy-card-common.js'; export default { @@ -388,6 +388,7 @@ export default { acts() { const raw = this.card && Array.isArray(this.card.acts) ? this.card.acts : []; const vis = normalizeActVis(this.c.actVis); + const del = normalizeActDeleted(this.c.actDeleted); const labels = normalizeActLabels(this.c.actLabels); const icons = this.c.actIcons && typeof this.c.actIcons === 'object' ? this.c.actIcons : {}; const order = normalizeActOrder(this.c.actOrder); @@ -409,12 +410,12 @@ export default { }); const out = []; order.forEach(t => { - if (byType[t] && vis[t] !== 0) out.push(merge(byType[t])); + if (byType[t] && vis[t] !== 0 && del[t] !== 1) out.push(merge(byType[t])); }); raw.forEach(a => { if (!a || !a.type) return; const t = String(a.type); - if (order.indexOf(t) >= 0 || vis[t] === 0) return; + if (order.indexOf(t) >= 0 || vis[t] === 0 || del[t] === 1) return; out.push(merge(a)); }); normalizeCustomActs(this.c.customActs).forEach(ca => { diff --git a/components-diy/diy-card-self.vue b/components-diy/diy-card-self.vue index f09e17e..5fad331 100644 --- a/components-diy/diy-card-self.vue +++ b/components-diy/diy-card-self.vue @@ -123,6 +123,7 @@ export default { qrImage: v.qrImage, // 2.19: 默认动作显隐/文案 + 自定义动作(基件 acts computed 合成) actVis: v.actVis, + actDeleted: v.actDeleted, actLabels: v.actLabels, actOrder: v.actOrder, customActs: v.customActs, diff --git a/components-diy/diy-card-show.vue b/components-diy/diy-card-show.vue index 7ca70b7..e8765e7 100644 --- a/components-diy/diy-card-show.vue +++ b/components-diy/diy-card-show.vue @@ -86,6 +86,7 @@ export default { qrImage: v.qrImage, // 2.19: 默认动作显隐/文案 + 自定义动作(基件 acts computed 合成) actVis: v.actVis, + actDeleted: v.actDeleted, actLabels: v.actLabels, actOrder: v.actOrder, customActs: v.customActs, diff --git a/tests/diy-card-style-v2.test.js b/tests/diy-card-style-v2.test.js index 93dab0a..e69fc25 100644 --- a/tests/diy-card-style-v2.test.js +++ b/tests/diy-card-style-v2.test.js @@ -363,7 +363,7 @@ test('反例自测: 新形态标记不得出现在未改造组件中', () => { * 2.19 快捷操作: 显示控制 + 自定义操作 + 素材图标库 + 图层式管理 * ------------------------------------------------------------------ */ -const V19_FIELDS = { actVis: 'object', actLabels: 'object', customActs: 'array', actOrder: 'array' }; +const V19_FIELDS = { actVis: 'object', actDeleted: 'object', actLabels: 'object', customActs: 'array', actOrder: 'array' }; const V19_ACT_LABELS = { tel: '打电话', wx: '微信号', mail: '发邮件', web: '网站', save: '存通讯录' }; test('diy_view.php: Show/Self value 均含 2.19 字段(默认值=全显/默认文案/空数组/历史顺序=零变化)', () => { @@ -379,6 +379,8 @@ test('diy_view.php: Show/Self value 均含 2.19 字段(默认值=全显/默认 assert.deepEqual(v.customActs, [], comp + '.customActs 默认空数组'); // actOrder(2.19.1): 历史渲染顺序 assert.deepEqual(v.actOrder, ['tel', 'wx', 'mail', 'web', 'save'], comp + '.actOrder 默认=历史顺序'); + // actDeleted(2.19.2): 全 0=无删除(历史零变化) + assert.deepEqual(v.actDeleted, { tel: 0, wx: 0, mail: 0, web: 0, save: 0 }, comp + '.actDeleted 默认全 0'); } }); @@ -432,13 +434,16 @@ test('common.js: normalizeActVis / normalizeActLabels / normalizeCustomActs / no js.match(/export const ACT_ORDER_DEFAULT = [^;]+;/)[0].replace('export const', 'const'), ].join('\n'); const grab = (name) => js.match(new RegExp('export function ' + name + '[\\s\\S]*?\\n}'))[0].replace('export function ', 'function '); - const body = consts + '\n' + ['normalizeActVis', 'normalizeActLabels', 'normalizeCustomActs', 'normalizeActOrder'].map(grab).join('\n') + '\n' - + 'return [normalizeActVis, normalizeActLabels, normalizeCustomActs, normalizeActOrder];'; - const [visFn, labelFn, customFn, orderFn] = new Function(body)(); + const body = consts + '\n' + ['normalizeActVis', 'normalizeActDeleted', 'normalizeActLabels', 'normalizeCustomActs', 'normalizeActOrder'].map(grab).join('\n') + '\n' + + 'return [normalizeActVis, normalizeActDeleted, normalizeActLabels, normalizeCustomActs, normalizeActOrder];'; + const [visFn, delFn, labelFn, customFn, orderFn] = new Function(body)(); // actVis: 缺省全显(历史零变化); '0' → 0; 未知键忽略 assert.deepEqual(visFn(undefined), { tel: 1, wx: 1, mail: 1, web: 1, save: 1 }, 'actVis 缺省全显'); assert.deepEqual(visFn({ tel: '0', wx: 0 }), { tel: 0, wx: 0, mail: 1, web: 1, save: 1 }, 'actVis 0=隐藏/缺键=显'); + // actDeleted(2.19.2): 缺省全 0(历史零变化); '1' → 1 + assert.deepEqual(delFn(undefined), { tel: 0, wx: 0, mail: 0, web: 0, save: 0 }, 'actDeleted 缺省全 0'); + assert.deepEqual(delFn({ tel: '1', save: 1 }), { tel: 1, wx: 0, mail: 0, web: 0, save: 1 }, 'actDeleted 1=已删/缺键=0'); // actLabels: 缺省=内置文案; 空串回落默认 assert.deepEqual(labelFn(undefined), V19_ACT_LABELS, 'actLabels 缺省=内置默认'); assert.deepEqual(labelFn({ tel: '', wx: '加微信' }).wx, '加微信', 'actLabels 改名生效'); @@ -492,8 +497,11 @@ test('mini.vue: acts 合成(显隐过滤 + 次序 + 文案覆盖 + 自定义动 assert.match(src, /acts\(\)\s*\{[\s\S]*?normalizeCustomActs\(this\.c\.customActs\)/, 'acts 消费 normalizeCustomActs'); // 2.19.1 次序: 按 normalizeActOrder 输出默认动作 + order 外扩展类型尾接兜底 assert.match(src, /acts\(\)\s*\{[\s\S]*?normalizeActOrder\(this\.c\.actOrder\)/, 'acts 消费 normalizeActOrder'); - assert.match(src, /acts\(\)\s*\{[\s\S]*?const byType = \{\};[\s\S]*?order\.forEach\(t => \{[\s\S]*?if \(byType\[t\] && vis\[t\] !== 0\) out\.push\(merge\(byType\[t\]\)\);/, '按 order 次序取默认动作(vis=0 过滤)'); - assert.match(src, /acts\(\)\s*\{[\s\S]*?if \(order\.indexOf\(t\) >= 0 \|\| vis\[t\] === 0\) return;/, 'order 外扩展类型尾接且隐藏不渲染'); + assert.match(src, /acts\(\)\s*\{[\s\S]*?const byType = \{\};[\s\S]*?order\.forEach\(t => \{/, 'acts 按 order 次序取默认动作'); + assert.match(src, /acts\(\)\s*\{[\s\S]*?if \(order\.indexOf\(t\) >= 0 \|\| vis\[t\] === 0 \|\| del\[t\] === 1\) return;/, 'order 外扩展类型尾接且隐藏/已删不渲染'); + // 2.19.2 删除: 按 order 取默认动作时 deleted=1 的不渲染 + assert.match(src, /acts\(\)\s*\{[\s\S]*?normalizeActDeleted\(this\.c\.actDeleted\)/, 'acts 消费 normalizeActDeleted'); + assert.match(src, /acts\(\)\s*\{[\s\S]*?if \(byType\[t\] && vis\[t\] !== 0 && del\[t\] !== 1\) out\.push\(merge\(byType\[t\]\)\);/, 'order 取默认动作时 vis=0/deleted=1 均过滤'); assert.match(src, /labels\[t\] \|\| a\.label/, 'actLabels 覆盖文案(空回落服务端)'); assert.match(src, /cfgImg: String\(cfg\.img \|\| ''\)\.trim\(\)/, '默认动作合并 cfgImg'); assert.match(src, /cfgKey: String\(cfg\.iconKey \|\| ''\)\.trim\(\)/, '默认动作合并 cfgKey(iconKey)'); @@ -503,11 +511,11 @@ test('mini.vue: acts 合成(显隐过滤 + 次序 + 文案覆盖 + 自定义动 assert.match(src, /actLibStyle\(act\)\s*\{[\s\S]*?libIconBg\(this\.actLibOf\(act\), this\.actIconColor\(act\)\)/, 'actLibStyle 消费 libIconBg + 四级色口径'); assert.equal((src.match(/v-else-if="actLibOf\(act\)" class="act-grid-svg" :style="actLibStyle\(act\)"/g) || []).length, 2, '上/下两份宫格副本均插入素材库分支'); assert.match(src, /actSvgOf\(act\)\s*\{[\s\S]*?!this\.actImgOf\(act\) && !this\.actLibOf\(act\) && !!WAP_LINE_ICONS/, '线稿档被 img/素材库优先占位后不再进入'); - assert.match(src, /import \{[\s\S]*?normalizeActVis, normalizeActLabels, normalizeCustomActs, normalizeActOrder, libIconBg/, 'mini.vue 引入 2.19+2.19.1 依赖'); + assert.match(src, /import \{[\s\S]*?normalizeActVis, normalizeActDeleted, normalizeActLabels, normalizeCustomActs, normalizeActOrder, libIconBg/, 'mini.vue 引入 2.19+2.19.1+2.19.2 依赖'); // 透传 for (const file of ['diy-card-show.vue', 'diy-card-self.vue']) { const vue = readFileSync(resolve(FE, file), 'utf8'); - for (const f of ['actVis', 'actLabels', 'actOrder', 'customActs']) { + for (const f of ['actVis', 'actDeleted', 'actLabels', 'actOrder', 'customActs']) { assert.match(vue, new RegExp(f + ': v\\.' + f + ','), file + ' 透传 ' + f); } } @@ -540,13 +548,18 @@ test('design.html ×2: 图层式管理面板 + 素材图标库 + 预览显隐/ assert.match(html, /nc\.customActs\.splice\(ci, 1\)/, comp + ' 删除自定义动作'); // 2.19.1 默认动作次序: 面板行按 actOrder 渲染 + ↑↓ 委托调整 assert.equal((html.match(/