diff --git a/components-diy/diy-card-common.js b/components-diy/diy-card-common.js index 7bb89a2..e1d04b2 100644 --- a/components-diy/diy-card-common.js +++ b/components-diy/diy-card-common.js @@ -353,8 +353,9 @@ export function hexToRgba(hex, alpha) { /** 默认动作类型序(图层面板行序与 C 端合并序一致) */ export const ACT_TYPES = ['tel', 'wx', 'mail', 'web', 'save']; -/** 自定义动作类型白名单(doAct 天然消费: tel/wx/mail/copy 分支 + else 兜底) */ -export const ACT_ACTION_TYPES = ['tel', 'wx', 'mail', 'web', 'copy']; +/** 自定义动作类型白名单(doAct 天然消费: tel/wx/mail/copy 分支 + else 兜底; + * 2.19.3 扩展: sms 短信/map 导航/link 页面链接(系统链接对话框)) */ +export const ACT_ACTION_TYPES = ['tel', 'wx', 'mail', 'web', 'copy', 'sms', 'map', 'link']; export const ACT_LABEL_DEFAULTS = { tel: '打电话', diff --git a/components-diy/diy-card-self.vue b/components-diy/diy-card-self.vue index 5fad331..e394880 100644 --- a/components-diy/diy-card-self.vue +++ b/components-diy/diy-card-self.vue @@ -217,6 +217,10 @@ export default { this.goSaveContact(); return; } + // map(导航): value 留空回落名片地址(2.19.3) + if (act.type === 'map' && !act.value && this.card && this.card.address) { + act = Object.assign({}, act, { value: this.card.address }); + } if (!act.value) return; if (act.type === 'tel') { uni.makePhoneCall({ phoneNumber: act.value, fail: () => {} }); @@ -235,6 +239,36 @@ export default { success: () => uni.showToast({ title: '邮箱已复制', icon: 'none' }), }); // #endif + } else if (act.type === 'sms') { + // #ifdef H5 + window.location.href = 'sms:' + act.value; + // #endif + // #ifndef H5 + uni.setClipboardData({ + data: act.value, + success: () => uni.showToast({ title: '号码已复制,请到短信粘贴', icon: 'none' }), + }); + // #endif + } else if (act.type === 'map') { + uni.setClipboardData({ + data: act.value, + success: () => uni.showToast({ title: '地址已复制,可粘贴到地图导航', icon: 'none' }), + }); + } else if (act.type === 'link') { + var isHttp = act.value.indexOf('http://') === 0 || act.value.indexOf('https://') === 0; + if (isHttp) { + // #ifdef H5 + window.location.href = act.value; + // #endif + // #ifndef H5 + uni.setClipboardData({ + data: act.value, + success: () => uni.showToast({ title: '链接已复制,请在浏览器打开', icon: 'none' }), + }); + // #endif + } else { + this.$util.redirectTo(act.value, {}); + } } else { uni.setClipboardData({ data: act.value, diff --git a/components-diy/diy-card-show.vue b/components-diy/diy-card-show.vue index e8765e7..ddb696b 100644 --- a/components-diy/diy-card-show.vue +++ b/components-diy/diy-card-show.vue @@ -129,6 +129,10 @@ export default { this.goSaveContact(); return; } + // map(导航): value 留空回落名片地址(2.19.3) + if (act.type === 'map' && !act.value && this.card && this.card.address) { + act = Object.assign({}, act, { value: this.card.address }); + } if (!act.value) return; if (act.type === 'tel') { uni.makePhoneCall({ phoneNumber: act.value, fail: () => {} }); @@ -147,6 +151,36 @@ export default { success: () => uni.showToast({ title: '邮箱已复制', icon: 'none' }), }); // #endif + } else if (act.type === 'sms') { + // #ifdef H5 + window.location.href = 'sms:' + act.value; + // #endif + // #ifndef H5 + uni.setClipboardData({ + data: act.value, + success: () => uni.showToast({ title: '号码已复制,请到短信粘贴', icon: 'none' }), + }); + // #endif + } else if (act.type === 'map') { + uni.setClipboardData({ + data: act.value, + success: () => uni.showToast({ title: '地址已复制,可粘贴到地图导航', icon: 'none' }), + }); + } else if (act.type === 'link') { + var isHttp = act.value.indexOf('http://') === 0 || act.value.indexOf('https://') === 0; + if (isHttp) { + // #ifdef H5 + window.location.href = act.value; + // #endif + // #ifndef H5 + uni.setClipboardData({ + data: act.value, + success: () => uni.showToast({ title: '链接已复制,请在浏览器打开', icon: 'none' }), + }); + // #endif + } else { + this.$util.redirectTo(act.value, {}); + } } else { uni.setClipboardData({ data: act.value, diff --git a/tests/diy-card-style-v2.test.js b/tests/diy-card-style-v2.test.js index e69fc25..0f70ba6 100644 --- a/tests/diy-card-style-v2.test.js +++ b/tests/diy-card-style-v2.test.js @@ -226,6 +226,11 @@ test('mini.vue + show/self: save(存通讯录)动作全链路(2.17.1)', () => { } assert.match(vue, /Array\.isArray\(c\.rows\)[\s\S]*?map\[r\.field\]/, file + ' 字段从 rows(白名单)提取'); assert.doesNotMatch(vue, /doAct\(act\)\s*\{\s*if \(!act \|\| !act\.value\) return;/, file + ' doAct 不再拦 value 为空的 save'); + // 2.19.3 扩展类型: sms/map/link 分支 + map 空值回落名片地址 + assert.match(vue, /act\.type === 'map' && !act\.value && this\.card && this\.card\.address/, file + ' map 空值回落名片地址'); + assert.match(vue, /act\.type === 'sms'\)\s*\{[\s\S]*?window\.location\.href = 'sms:' \+ act\.value;[\s\S]*?号码已复制/, file + ' sms 分支(H5 sms: 链接/非 H5 复制)'); + assert.match(vue, /act\.type === 'map'\)\s*\{[\s\S]*?地址已复制,可粘贴到地图导航/, file + ' map 分支(复制地址)'); + assert.match(vue, /act\.type === 'link'\)\s*\{[\s\S]*?indexOf\('http:\/\/'\) === 0 [\s\S]*?\$util\.redirectTo\(act\.value, \{\}\)/, file + ' link 分支(外链 H5 跳转/小程序复制, 站内 redirectTo)'); } }); @@ -461,6 +466,8 @@ test('common.js: normalizeActVis / normalizeActLabels / normalizeCustomActs / no assert.equal(list[0].label, '加群', 'label 保留'); assert.equal(list[1].label, '自定义', 'label 缺省'); assert.equal(list[1].type, 'tel', '合法 type 保留'); + // 2.19.3 扩展类型白名单 + assert.deepEqual(customFn([{ type: 'sms', value: '139' }, { type: 'map', value: '某某大厦' }, { type: 'link', value: '/pages/index/index' }]).map(x => x.type), ['sms', 'map', 'link'], 'sms/map/link 新类型通过白名单'); // actOrder(2.19.1): 缺省=历史顺序; 去重+白名单+缺失尾补 assert.deepEqual(orderFn(undefined), ['tel', 'wx', 'mail', 'web', 'save'], 'actOrder 缺省=历史顺序'); assert.deepEqual(orderFn(['save', 'tel', 'bogus', 'save', 'mail']), ['save', 'tel', 'mail', 'wx', 'web'], '重排去重+非法剔除+缺失尾补'); @@ -541,6 +548,8 @@ test('design.html ×2: 图层式管理面板 + 素材图标库 + 预览显隐/ // 自定义动作行: 类型 chips / 取值 / 排序 / 删除 / 添加 assert.match(html, /diy-card-layer--custom" v-for="\(c, ci\) in nc\.customActs"/, comp + ' 自定义动作图层行'); assert.match(html, /:class="\{ active: c\.type === t\.v \}"/, comp + ' 自定义动作类型 chips'); + assert.match(html, /\{v:'copy',lb:'复制'\},\{v:'sms',lb:'短信'\},\{v:'map',lb:'导航'\},\{v:'link',lb:'页面'\}\]/, comp + ' 类型 chips 含 2.19.3 扩展(短信/导航/页面, web 文案改网址)'); + assert.match(html, /js-act-syslink" data-scope="custom" :data-idx="ci" v-if="c\.type === 'link'"/, comp + ' link 类型出现「系统链接」选择按钮'); assert.match(html, /v-model="c\.value" maxlength="200"/, comp + ' 自定义动作取值输入'); assert.match(html, /js-act-lib" data-scope="custom" :data-idx="ci"/, comp + ' 素材图标按钮(自定义动作)'); assert.match(html, /nc\.customActs\.splice\(ci - 1, 0, nc\.customActs\.splice\(ci, 1\)\[0\]\)/, comp + ' 上移排序'); @@ -612,6 +621,11 @@ test('card-act-icon.js: 类名委托修 2.18 选图标 Bug + $set 写回 + 素 assert.match(src, /iconKey: '', svg: '', color: ''/, '缺行防御补齐含 svg 键'); assert.match(src, /window\.CARD_ICON_LIB \|\| \{\}/, '弹层渲染消费同源图标库'); assert.match(src, /cal-item" data-key=/, '弹层图标网格'); + // 2.19.3 系统链接对话框委托(link 类型) + assert.match(src, /on\('click', '\.js-act-syslink'/, '系统链接走类名委托'); + assert.match(src, /ns\.select_link\(\{\}, function \(data\)/, '调全局 ns.select_link 打开系统链接对话框'); + assert.match(src, /data\.wap_url \|\| data\.url/, '回执取 wap_url 写入 value'); + assert.match(src, /window\.vue\.\$set\(target\.item, 'value', url\)/, '系统链接结果 $set 写回 value'); // 反例: 面板不再内联调 openAlbum 选快捷操作图标(2.18 Bug 根因规避) for (const comp of ['show', 'self']) { const html = readFileSync(resolve(BE, 'component/view/diy-card-' + comp + '/design.html'), 'utf8');