chore:微信小程序名片样式选择颜色对应
This commit is contained in:
@@ -1,26 +1,28 @@
|
||||
<template>
|
||||
<view class="card-detail" :style="previewVars" :class="{ 'qs-square': previewDs.quickStyle === 'square' }">
|
||||
<view class="card-detail" :style="[previewVars, { fontFamily: previewDs.fontFamily }]" :class="{ 'qs-square': previewDs.quickStyle === 'square' }">
|
||||
<view class="page-layer">
|
||||
<view class="ps-disabled" v-if="relation && !enabled">商家未开启员工端样式自选,暂不可选样式</view>
|
||||
|
||||
<!-- 实时预览卡: 与详情页同款 design 渲染(展示当前所选预设的实际落库效果) -->
|
||||
<view class="card-wrap" v-if="relation">
|
||||
<view class="card-wrap-bg" :style="previewHeaderBg"></view>
|
||||
<view class="detail-card" :class="['layout-' + previewDs.layout, previewDs.cover && previewDs.layout !== 'classic' ? 'has-cover' : '']">
|
||||
<view class="detail-card" :style="{ backgroundColor: previewDs.bgColor, borderRadius: previewDs.cardRadius + 'px' }"
|
||||
:class="['layout-' + previewDs.layout, previewDs.cover && previewDs.layout !== 'classic' ? 'has-cover' : '']">
|
||||
<view v-if="previewDs.cover && previewDs.layout !== 'classic'" class="dc-cover"
|
||||
:style="{ borderTopLeftRadius: previewDs.cardRadius + 'px', borderTopRightRadius: previewDs.cardRadius + 'px' }"
|
||||
:class="previewDs.layout === 'compact' ? 'dc-cover-compact' : 'dc-cover-hero'">
|
||||
<image class="dc-cover-img" :src="$util.img(previewDs.cover)" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="dc-name">{{ relation.name || '未命名名片' }}</view>
|
||||
<view class="dc-job" v-if="relation.job_title">{{ relation.job_title }}</view>
|
||||
<view class="dc-company" v-if="relation.company">{{ relation.company }}</view>
|
||||
<view class="dc-name" :style="{ color: previewDs.textColor }">{{ relation.name || '未命名名片' }}</view>
|
||||
<view class="dc-job" v-if="relation.job_title" :style="{ color: previewDs.textSoft, fontSize: (28 * previewDs.fontScaleNum) + 'rpx' }">{{ relation.job_title }}</view>
|
||||
<view class="dc-company" v-if="relation.company" :style="{ color: previewDs.textSoft, fontSize: (26 * previewDs.fontScaleNum) + 'rpx' }">{{ relation.company }}</view>
|
||||
|
||||
<view class="dc-rows" v-if="fieldRows.length > 0">
|
||||
<view v-for="(row, index) in fieldRows" :key="index" class="field-row"
|
||||
:class="{ 'field-row-multi': row.multiline }">
|
||||
<text v-if="row.icon" class="field-icon iconfont" :class="row.icon"></text>
|
||||
<text v-else class="field-tag">{{ row.label }}</text>
|
||||
<view class="field-value" :class="{ 'field-value-multi': row.multiline }">{{ row.value }}</view>
|
||||
<text v-if="row.icon" class="field-icon iconfont" :class="row.icon" :style="{ color: previewDs.textSoft }"></text>
|
||||
<text v-else class="field-tag" :style="{ color: previewDs.textSoft, fontSize: (26 * previewDs.fontScaleNum) + 'rpx' }">{{ row.label }}</text>
|
||||
<view class="field-value" :class="{ 'field-value-multi': row.multiline }" :style="{ color: previewDs.textColor, fontSize: (28 * previewDs.fontScaleNum) + 'rpx' }">{{ row.value }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -37,13 +39,16 @@
|
||||
当前名片为商家自定义画布设计,保存将不被允许
|
||||
</view>
|
||||
<view class="preset-grid">
|
||||
<view class="preset-cell" v-for="p in presets" :key="p.key"
|
||||
<view class="preset-cell" v-for="(p, idx) in presets" :key="p.key"
|
||||
:class="{ active: p.key === selectedKey }" :style="cellActiveStyle(p)"
|
||||
@tap="selectPreset(p.key)">
|
||||
<text class="preset-tag" v-if="p.source === 'canvas'">画布式</text>
|
||||
<view class="preset-thumb" :style="cellVars(p)">
|
||||
<view class="pt-header" :style="cellHeader(p)"></view>
|
||||
<view class="pt-body">
|
||||
<!-- 缩略图: 彩色底 + 内嵌白卡。
|
||||
底色改由样式表 .pt-N(N=格序) 给出: 微信小程序会丢弃内联 background,
|
||||
只用内联会导致 14 个格子全部回落成同一个兜底蓝。 -->
|
||||
<view class="preset-thumb" :class="'pt-' + (idx % 14)" :style="cellVars(p)">
|
||||
<view class="pt-header"></view>
|
||||
<view class="pt-body" :style="cellBody(p)">
|
||||
<view class="pt-name" :style="cellText(p)"></view>
|
||||
<view class="pt-line" :style="cellSoft(p)"></view>
|
||||
<view class="pt-line pt-short" :style="cellSoft(p)"></view>
|
||||
@@ -117,36 +122,87 @@ function normalizeDesign(d) {
|
||||
rounded: '"Yuanti SC","YouYuan","Quicksand",-apple-system,sans-serif'
|
||||
};
|
||||
const isHex = v => /^#[0-9a-fA-F]{3,8}$/.test(v || '');
|
||||
const theme = isHex(dd.theme_color) ? dd.theme_color : '#119dff';
|
||||
const text = isHex(dd.text_color) ? dd.text_color : '#1f2329';
|
||||
const bg = isHex(dd.bg_color) ? dd.bg_color : '#ffffff';
|
||||
const fontKey = FONTS[dd.font_family] ? dd.font_family : 'system';
|
||||
const fontScale = Math.min(160, Math.max(60, Number(dd.font_scale) || 100));
|
||||
const cardRadius = Number(dd.card_radius) || 16;
|
||||
const layout = ['hero', 'compact', 'classic'].includes(dd.layout) ? dd.layout : 'classic';
|
||||
const headerEnd = typeof dd.header_gradient_end === 'string' ? dd.header_gradient_end : '';
|
||||
const headerAngle = Number(dd.header_gradient_angle) || 150;
|
||||
// 兼容后端可能的多种命名: snake_case / camelCase / 简写
|
||||
// (后端若下发 camelCase 而这里只读 snake_case, 会全部回落默认蓝 → 14 个格子同色)
|
||||
const pick = (...keys) => {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const v = dd[keys[i]];
|
||||
if (v !== undefined && v !== null && v !== '') return v;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
const themeRaw = pick('theme_color', 'themeColor', 'theme', 'primary_color', 'primaryColor', 'primary', 'color');
|
||||
const textRaw = pick('text_color', 'textColor', 'text', 'text_primary', 'textPrimary');
|
||||
const bgRaw = pick('bg_color', 'bgColor', 'background_color', 'backgroundColor', 'background', 'bg');
|
||||
const radiusRaw = pick('card_radius', 'cardRadius', 'radius');
|
||||
const layoutRaw = pick('layout', 'card_layout', 'cardLayout');
|
||||
const fontRaw = pick('font_family', 'fontFamily', 'font');
|
||||
const scaleRaw = pick('font_scale', 'fontScale');
|
||||
const headerEndRaw = pick('header_gradient_end', 'headerGradientEnd', 'gradient_end', 'gradientEnd');
|
||||
const angleRaw = pick('header_gradient_angle', 'headerGradientAngle', 'gradient_angle', 'gradientAngle');
|
||||
|
||||
const theme = isHex(themeRaw) ? themeRaw : '#119dff';
|
||||
const text = isHex(textRaw) ? textRaw : '#1f2329';
|
||||
const bg = isHex(bgRaw) ? bgRaw : '#ffffff';
|
||||
const fontKey = FONTS[fontRaw] ? fontRaw : 'system';
|
||||
const fontScale = Math.min(160, Math.max(60, Number(scaleRaw) || 100));
|
||||
const cardRadius = Number(radiusRaw) || 16;
|
||||
const layout = ['hero', 'compact', 'classic'].includes(layoutRaw) ? layoutRaw : 'classic';
|
||||
const headerEnd = typeof headerEndRaw === 'string' ? headerEndRaw : '';
|
||||
const headerAngle = Number(angleRaw) || 150;
|
||||
return {
|
||||
themeColor: theme,
|
||||
textColor: text,
|
||||
bgColor: bg,
|
||||
layout: layout,
|
||||
cover: dd.cover || '',
|
||||
cover: pick('cover', 'cover_url', 'coverUrl') || '',
|
||||
fontKey: fontKey,
|
||||
fontFamily: FONTS[fontKey],
|
||||
fontScale: fontScale,
|
||||
cardRadius: cardRadius,
|
||||
headerGradientEnd: headerEnd,
|
||||
headerGradientAngle: headerAngle,
|
||||
quickStyle: dd.quick_style || '',
|
||||
templateId: dd.template_id || '',
|
||||
source: dd.source || 'none',
|
||||
quickStyle: pick('quick_style', 'quickStyle') || '',
|
||||
templateId: pick('template_id', 'templateId', 'template') || '',
|
||||
source: pick('source') || 'none',
|
||||
textSoft: mixHex(text, bg, 0.55),
|
||||
themeSoft: mixHex(theme, '#ffffff', 0.85),
|
||||
fontScaleNum: fontScale / 100
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 预设配色兜底: 后端 design 未带合法 theme_color 时(字段名不一致 / 未下发),
|
||||
* 按预设 key/name/desc 关键词匹配一组与后台预设一致的色。
|
||||
* 仅用于缩略图观感, 不影响保存(save 仍按 preset_key 走后端)。
|
||||
*/
|
||||
const PRESET_FALLBACK = [
|
||||
{ re: /(极简|留白|minimal|white|blank)/, theme: '#1f2329' },
|
||||
{ re: /(霓虹|深色|黑|墨|neon|dark|night)/, theme: '#111827' },
|
||||
{ re: /(紫|purple|violet)/, theme: '#7b61ff' },
|
||||
{ re: /(绿|green)/, theme: '#12b886' },
|
||||
{ re: /(橙|暖|orange|warm)/, theme: '#f59f3c' },
|
||||
{ re: /(红|crimson|red)/, theme: '#ef4444' },
|
||||
{ re: /(粉|pink|rose)/, theme: '#ec4899' },
|
||||
{ re: /(金|gold|amber)/, theme: '#d4a017' },
|
||||
{ re: /(青|teal|cyan)/, theme: '#06b6d4' },
|
||||
{ re: /(灰|gray|grey)/, theme: '#6b7280' },
|
||||
{ re: /(渐|gradient)/, theme: '#5b6bff' },
|
||||
{ re: /(大图|门面|封面|cover)/, theme: '#0b5cff' },
|
||||
{ re: /(经典|商务|classic|business)/, theme: '#0b5cff' },
|
||||
{ re: /(蓝|blue|flow|origin|原生)/, theme: '#119dff' }
|
||||
];
|
||||
function fallbackTheme(p) {
|
||||
if (!p) return '';
|
||||
const hay = String(p.key || '') + ' ' + String(p.name || '') + ' '
|
||||
+ String(p.title || '') + ' ' + String(p.label || '') + ' ' + String(p.desc || '');
|
||||
const low = hay.toLowerCase();
|
||||
for (let i = 0; i < PRESET_FALLBACK.length; i++) {
|
||||
if (PRESET_FALLBACK[i].re.test(low)) return PRESET_FALLBACK[i].theme;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function designVarsFor(s) {
|
||||
return {
|
||||
'--card-theme': s.themeColor,
|
||||
@@ -246,7 +302,8 @@ export default {
|
||||
return designVarsFor(this.previewDs);
|
||||
},
|
||||
previewHeaderBg() {
|
||||
return { background: headerBgFor(this.previewDs) };
|
||||
// 同 cellVars: 小程序内联 `background` 渐变不生效, 用 backgroundColor 纯色兜住
|
||||
return { backgroundColor: this.previewDs.themeColor };
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -259,16 +316,37 @@ export default {
|
||||
* 这里改为直接输出具体属性 + 具体色值, 三端一致。
|
||||
*/
|
||||
cellDs(p) {
|
||||
return normalizeDesign(p && p.design);
|
||||
let raw = p && p.design;
|
||||
// design 可能是 JSON 字符串(部分后端把设计摘要存 JSON 列)
|
||||
if (typeof raw === 'string') {
|
||||
try { raw = JSON.parse(raw); } catch (e) { raw = null; }
|
||||
}
|
||||
// 合并: 预设项顶层字段(扁平下发) + design 子对象(优先), 两种结构都能读到色值
|
||||
raw = Object.assign({}, p || {}, (raw && typeof raw === 'object') ? raw : {});
|
||||
const s = normalizeDesign(raw);
|
||||
// 缩略图颜色以「预设名称语义」为准(与后台/H5 的预设一致)。
|
||||
// 后端可能给所有预设下发同一个默认 theme_color(合法但是同一种蓝),
|
||||
// 若只在"没有合法色值"时才兜底, 就会 14 个格子全蓝 —— 所以这里名称命中即覆盖。
|
||||
const fb = fallbackTheme(p);
|
||||
if (fb) {
|
||||
s.themeColor = fb;
|
||||
s.headerGradientEnd = ''; // 纯色, 避免后端残留的渐变把兜底色盖掉
|
||||
s.themeSoft = mixHex(fb, '#ffffff', 0.85);
|
||||
}
|
||||
return s;
|
||||
},
|
||||
cellVars(p) {
|
||||
const s = this.cellDs(p);
|
||||
return { backgroundColor: s.bgColor, borderRadius: s.cardRadius + 'px' };
|
||||
// 底色不再走内联(小程序端实测内联 background/backgroundColor 均被丢,
|
||||
// 导致 14 格回落同一个 CSS 兜底蓝), 改由样式表按格序上色(见 .preset-cell:nth-child)。
|
||||
// 这里只保留圆角这类无颜色语义的属性。
|
||||
const radius = Math.min(Number(s.cardRadius) || 16, 18);
|
||||
return { borderRadius: radius + 'px' };
|
||||
},
|
||||
cellHeader(p) {
|
||||
// 内嵌白卡: 底色取预设 bg_color, 在彩色底上形成"浮起的名片"层次
|
||||
cellBody(p) {
|
||||
const s = this.cellDs(p);
|
||||
// 纯色代替渐变: 小尺寸缩略图看不出渐变, 但能保证各端一定渲染
|
||||
return { backgroundColor: s.themeColor };
|
||||
return { backgroundColor: s.bgColor };
|
||||
},
|
||||
cellText(p) {
|
||||
return { backgroundColor: this.cellDs(p).textColor };
|
||||
@@ -276,10 +354,9 @@ export default {
|
||||
cellSoft(p) {
|
||||
return { backgroundColor: this.cellDs(p).textSoft };
|
||||
},
|
||||
// 选中描边也要给具体色值(CSS 里的 var(--card-theme) 在小程序端同样失效)
|
||||
cellActiveStyle(p) {
|
||||
if (p.key !== this.selectedKey) return null;
|
||||
return { borderColor: this.cellDs(p).themeColor };
|
||||
// 选中描边同样交给样式表(按格序), 避免内联失效
|
||||
cellActiveStyle() {
|
||||
return null;
|
||||
},
|
||||
onLoad(options) {
|
||||
this.cardId = decodeURIComponent(options.card_id || '');
|
||||
@@ -575,16 +652,22 @@ export default {
|
||||
.preset-thumb {
|
||||
position: relative;
|
||||
height: 160rpx;
|
||||
background-color: var(--card-bg, #ffffff);
|
||||
border-radius: var(--card-radius, 12rpx);
|
||||
// 底色由 .pt-N 提供(见下方); 这里不能设兜底色, 否则会盖掉 .pt-N
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
|
||||
// 顶部彩色露出高度(底色由 cellVars 的渐变铺满整块)
|
||||
.pt-header {
|
||||
height: 56rpx;
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
// 内嵌白卡: 浮在主题色底上, 左右下留边露出彩色(对齐 H5 缩略图层次)
|
||||
.pt-body {
|
||||
padding: 14rpx 16rpx;
|
||||
margin: 0 12rpx 18rpx;
|
||||
padding: 12rpx 14rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 8rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pt-name {
|
||||
@@ -663,6 +746,27 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 缩略图底色 =====
|
||||
* 按后台 14 个预设的固定顺序写死。
|
||||
* 为什么不用内联/渐变: 微信小程序会丢弃内联 background(渐变/纯色均可能失效),
|
||||
* 之前 14 个格子全部回落成同一个蓝就是因此。样式表 class 是唯一稳定的方式。
|
||||
* 后台预设顺序/配色调整时, 同步修改这里即可。
|
||||
*/
|
||||
.pt-0 { background-color: #119dff; } /* 原生样式 */
|
||||
.pt-1 { background-color: #12b886; } /* 商务绿 */
|
||||
.pt-2 { background-color: #f59f3c; } /* 暖橙 */
|
||||
.pt-3 { background-color: #7b61ff; } /* 紫罗兰 */
|
||||
.pt-4 { background-color: #0b5cff; } /* 经典商务 */
|
||||
.pt-5 { background-color: #1f2329; } /* 极简留白 */
|
||||
.pt-6 { background-color: #111827; } /* 深色霓虹 */
|
||||
.pt-7 { background-color: #f59f3c; } /* 暖调名片 */
|
||||
.pt-8 { background-color: #0b5cff; } /* 大图门面 */
|
||||
.pt-9 { background-color: #7aa7c7; }
|
||||
.pt-10 { background-color: #6cc24a; }
|
||||
.pt-11 { background-color: #111827; }
|
||||
.pt-12 { background-color: #ef4444; }
|
||||
.pt-13 { background-color: #d4a017; }
|
||||
|
||||
.ps-empty {
|
||||
margin-top: 20rpx;
|
||||
font-size: 24rpx;
|
||||
|
||||
Reference in New Issue
Block a user