|
@@ -40,7 +40,6 @@
|
|
|
'--fill-font-family': data.property.fill_font,
|
|
'--fill-font-family': data.property.fill_font,
|
|
|
width: (inputWidthMap[li.mark] || data.property.input_default_width) + 'px',
|
|
width: (inputWidthMap[li.mark] || data.property.input_default_width) + 'px',
|
|
|
marginBottom: resolveFillControlMargin(li),
|
|
marginBottom: resolveFillControlMargin(li),
|
|
|
- ...getFillControlAlignmentStyle(li),
|
|
|
|
|
},
|
|
},
|
|
|
]"
|
|
]"
|
|
|
@input="handleInput(li.input, li.mark)"
|
|
@input="handleInput(li.input, li.mark)"
|
|
@@ -311,47 +310,42 @@ export default {
|
|
|
},
|
|
},
|
|
|
/**
|
|
/**
|
|
|
* 计算填空控件需要补偿的 margin-bottom。
|
|
* 计算填空控件需要补偿的 margin-bottom。
|
|
|
- * 词级 margin-bottom 会撑高行盒,控件 vertical-align: bottom 会贴到被撑高的行盒底部,
|
|
|
|
|
- * 因此给控件加上与段落一致的 margin-bottom,使其边框底部回到文本底部;
|
|
|
|
|
- * 无 margin 时返回空字符串,保持原有显示不变。
|
|
|
|
|
|
|
+ * PinyinText 会按 line-height - 1.5em 为词级元素增加 margin-bottom。
|
|
|
|
|
+ * 输入框需要使用相同补偿,才能与词级文本处于同一视觉基线。
|
|
|
* @param {Object} li 填空块对象
|
|
* @param {Object} li 填空块对象
|
|
|
- * @returns {String} 补偿值,如 '28pt',无则空字符串
|
|
|
|
|
|
|
+ * @returns {String} 与拼音词块行间距一致的补偿值
|
|
|
*/
|
|
*/
|
|
|
resolveFillControlMargin(li) {
|
|
resolveFillControlMargin(li) {
|
|
|
- const tagItem = (li?.rich_text_list || []).find((item) =>
|
|
|
|
|
- /^<(p|div|h[1-6]|li|blockquote)\b[^>]*>/i.test(String(item?.text || '')),
|
|
|
|
|
- );
|
|
|
|
|
- const raw = String(tagItem?.text || '').match(/margin-bottom\s*:\s*([^;"']+)/i);
|
|
|
|
|
- if (!raw) return '';
|
|
|
|
|
- const match = raw[1].trim().match(/^([\d.]+)\s*(px|pt|em)?$/i);
|
|
|
|
|
- if (!match) return '';
|
|
|
|
|
- const amount = parseFloat(match[1]);
|
|
|
|
|
- const unit = (match[2] || 'px').toLowerCase();
|
|
|
|
|
- // px/pt 为绝对单位,可直接使用;em 相对词级字号,换算为绝对 pt 后再补偿
|
|
|
|
|
- if (unit !== 'em') return raw[1].trim();
|
|
|
|
|
- const fontSize = String(this.pinyinBodyStyles?.fontSize || '16pt');
|
|
|
|
|
- const fsMatch = fontSize.match(/^([\d.]+)\s*(px|pt|em)?$/i);
|
|
|
|
|
- if (!fsMatch) return raw[1].trim();
|
|
|
|
|
- const fsAmount = parseFloat(fsMatch[1]);
|
|
|
|
|
- const fsUnit = (fsMatch[2] || 'pt').toLowerCase();
|
|
|
|
|
- if (fsUnit === 'em') return `${amount * fsAmount}em`;
|
|
|
|
|
- if (fsUnit === 'pt') return `${amount * fsAmount}pt`;
|
|
|
|
|
- return `${amount * fsAmount}px`;
|
|
|
|
|
- },
|
|
|
|
|
- getFillControlAlignmentStyle(li) {
|
|
|
|
|
- const tagItem = (li?.rich_text_list || []).find((item) =>
|
|
|
|
|
- /^<(p|div|h[1-6]|li|blockquote)\b[^>]*>/i.test(String(item?.text || '')),
|
|
|
|
|
- );
|
|
|
|
|
- const lineHeight = String(tagItem?.text || '').match(/line-height\s*:\s*([^;"']+)/i);
|
|
|
|
|
- if (!lineHeight || !/^\d*\.?\d+$/.test(lineHeight[1].trim())) return {};
|
|
|
|
|
-
|
|
|
|
|
- const ratio = Number(lineHeight[1].trim());
|
|
|
|
|
- if (ratio < 0 || ratio >= 1.5) return {};
|
|
|
|
|
- return {
|
|
|
|
|
- marginBottom: '',
|
|
|
|
|
- position: 'relative',
|
|
|
|
|
- top: `${1.5 - ratio}em`,
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ let lineHeight = '';
|
|
|
|
|
+ (li?.rich_text_list || []).forEach((item) => {
|
|
|
|
|
+ const match = String(item?.text || '').match(/line-height\s*:\s*([^;"']+)/i);
|
|
|
|
|
+ if (match) lineHeight = match[1].trim();
|
|
|
|
|
+ });
|
|
|
|
|
+ if (!/^\d*\.?\d+$/.test(lineHeight)) return '';
|
|
|
|
|
+
|
|
|
|
|
+ const ratio = Number(lineHeight);
|
|
|
|
|
+ const spacing = ratio - 1.5;
|
|
|
|
|
+ const lineHeightOffsets = [
|
|
|
|
|
+ { ratio: 1, offset: -6 },
|
|
|
|
|
+ { ratio: 1.2, offset: -3 },
|
|
|
|
|
+ { ratio: 1.5, offset: -2 },
|
|
|
|
|
+ { ratio: 2, offset: 2 },
|
|
|
|
|
+ { ratio: 2.5, offset: 6 },
|
|
|
|
|
+ { ratio: 3, offset: 10 },
|
|
|
|
|
+ ];
|
|
|
|
|
+ const lower = [...lineHeightOffsets].reverse().find((item) => item.ratio <= ratio);
|
|
|
|
|
+ const upper = lineHeightOffsets.find((item) => item.ratio >= ratio);
|
|
|
|
|
+ let offset = 0;
|
|
|
|
|
+ if (!lower || !upper) {
|
|
|
|
|
+ offset = lineHeightOffsets[ratio < 1 ? 0 : lineHeightOffsets.length - 1].offset;
|
|
|
|
|
+ } else if (lower.ratio === upper.ratio) {
|
|
|
|
|
+ offset = lower.offset;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ offset = lower.offset + ((ratio - lower.ratio) / (upper.ratio - lower.ratio)) * (upper.offset - lower.offset);
|
|
|
|
|
+ }
|
|
|
|
|
+ const offsetExpression = offset < 0 ? ` - ${Math.abs(offset)}px` : ` + ${offset}px`;
|
|
|
|
|
+
|
|
|
|
|
+ return `calc(${spacing}em${offsetExpression})`;
|
|
|
},
|
|
},
|
|
|
handleWav(data) {
|
|
handleWav(data) {
|
|
|
this.data.record_list = data;
|
|
this.data.record_list = data;
|