优化模版
This commit is contained in:
parent
67eb412268
commit
c17182527d
@ -112,9 +112,6 @@
|
||||
.preview-content {
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
font-size: 15px;
|
||||
line-height: 1.75;
|
||||
color: hsl(var(--foreground));
|
||||
padding:1rem 1.5rem;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
|
@ -200,290 +200,64 @@ export default function WechatEditor() {
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建临时容器来处理样式
|
||||
const tempDiv = document.createElement('div')
|
||||
tempDiv.innerHTML = previewContent.innerHTML
|
||||
|
||||
// 获取当前模板
|
||||
const template = templates.find(t => t.id === selectedTemplate)
|
||||
if (template) {
|
||||
// 添加基础样式容器
|
||||
const styleContainer = document.createElement('div')
|
||||
styleContainer.style.cssText = `
|
||||
max-width: 780px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
`
|
||||
styleContainer.innerHTML = tempDiv.innerHTML
|
||||
tempDiv.innerHTML = ''
|
||||
tempDiv.appendChild(styleContainer)
|
||||
|
||||
// 处理 CSS 变量
|
||||
const cssVariables = {
|
||||
'--md-primary-color': styleOptions.base?.primaryColor || template.options.base?.primaryColor || '#333333',
|
||||
'--foreground': styleOptions.base?.themeColor || template.options.base?.themeColor || '#1a1a1a',
|
||||
'--background': '#ffffff',
|
||||
'--muted': '#f1f5f9',
|
||||
'--muted-foreground': '#64748b',
|
||||
'--border': '#e2e8f0',
|
||||
'--ring': '#3b82f6',
|
||||
'--accent': '#f8fafc',
|
||||
'--accent-foreground': '#0f172a'
|
||||
}
|
||||
|
||||
// 替换 CSS 变量的函数
|
||||
const replaceCSSVariables = (value: string) => {
|
||||
let result = value
|
||||
Object.entries(cssVariables).forEach(([variable, replaceValue]) => {
|
||||
// 处理 var(--xxx) 格式
|
||||
result = result.replace(
|
||||
new RegExp(`var\\(${variable}\\)`, 'g'),
|
||||
replaceValue
|
||||
)
|
||||
// 处理 hsl(var(--xxx)) 格式
|
||||
result = result.replace(
|
||||
new RegExp(`hsl\\(var\\(${variable}\\)\\)`, 'g'),
|
||||
replaceValue
|
||||
)
|
||||
// 处理 rgb(var(--xxx)) 格式
|
||||
result = result.replace(
|
||||
new RegExp(`rgb\\(var\\(${variable}\\)\\)`, 'g'),
|
||||
replaceValue
|
||||
)
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// 合并基础样式
|
||||
const baseStyles = {
|
||||
fontSize: template.options.base?.fontSize || '15px',
|
||||
lineHeight: template.options.base?.lineHeight || '1.75',
|
||||
textAlign: template.options.base?.textAlign || 'left',
|
||||
color: template.options.base?.themeColor || '#1a1a1a',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
|
||||
...(styleOptions.base || {})
|
||||
}
|
||||
|
||||
// 处理基础样式中的 CSS 变量
|
||||
Object.entries(baseStyles).forEach(([key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
baseStyles[key] = replaceCSSVariables(value)
|
||||
}
|
||||
})
|
||||
Object.assign(styleContainer.style, baseStyles)
|
||||
|
||||
// 应用基础样式到所有文本元素
|
||||
const applyBaseStyles = () => {
|
||||
const textElements = styleContainer.querySelectorAll('h1, h2, h3, h4, h5, h6, p, blockquote, li, code, strong, em, a, span')
|
||||
textElements.forEach(element => {
|
||||
if (element instanceof HTMLElement) {
|
||||
// 如果元素没有特定的字体和字号设置,应用基础样式
|
||||
if (!element.style.fontFamily) {
|
||||
element.style.fontFamily = baseStyles.fontFamily
|
||||
}
|
||||
if (!element.style.fontSize) {
|
||||
// 根据元素类型设置不同的字号
|
||||
const tagName = element.tagName.toLowerCase()
|
||||
if (tagName === 'h1') {
|
||||
element.style.fontSize = '24px'
|
||||
} else if (tagName === 'h2') {
|
||||
element.style.fontSize = '20px'
|
||||
} else if (tagName === 'h3') {
|
||||
element.style.fontSize = '18px'
|
||||
} else if (tagName === 'code') {
|
||||
element.style.fontSize = '14px'
|
||||
} else {
|
||||
element.style.fontSize = baseStyles.fontSize
|
||||
}
|
||||
}
|
||||
// 确保颜色和行高也被应用
|
||||
if (!element.style.color) {
|
||||
element.style.color = baseStyles.color
|
||||
}
|
||||
if (!element.style.lineHeight) {
|
||||
element.style.lineHeight = baseStyles.lineHeight as string
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 合并块级样式
|
||||
const applyBlockStyles = (selector: string, templateStyles: any, customStyles: any) => {
|
||||
styleContainer.querySelectorAll(selector).forEach(element => {
|
||||
const effectiveStyles: Record<string, string> = {}
|
||||
|
||||
// 应用模板样式
|
||||
if (templateStyles) {
|
||||
Object.entries(templateStyles).forEach(([key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
effectiveStyles[key] = replaceCSSVariables(value)
|
||||
} else {
|
||||
effectiveStyles[key] = value as string
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 应用自定义样式
|
||||
if (customStyles) {
|
||||
Object.entries(customStyles).forEach(([key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
effectiveStyles[key] = replaceCSSVariables(value)
|
||||
} else {
|
||||
effectiveStyles[key] = value as string
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 确保字体样式被应用
|
||||
if (!effectiveStyles.fontFamily) {
|
||||
effectiveStyles.fontFamily = baseStyles.fontFamily
|
||||
}
|
||||
// 确保字号被应用
|
||||
if (!effectiveStyles.fontSize) {
|
||||
const tagName = (element as HTMLElement).tagName.toLowerCase()
|
||||
if (tagName === 'h1') {
|
||||
effectiveStyles.fontSize = '24px'
|
||||
} else if (tagName === 'h2') {
|
||||
effectiveStyles.fontSize = '20px'
|
||||
} else if (tagName === 'h3') {
|
||||
effectiveStyles.fontSize = '18px'
|
||||
} else if (tagName === 'code') {
|
||||
effectiveStyles.fontSize = '14px'
|
||||
} else {
|
||||
effectiveStyles.fontSize = baseStyles.fontSize
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign((element as HTMLElement).style, effectiveStyles)
|
||||
})
|
||||
}
|
||||
|
||||
// 应用标题样式
|
||||
;['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].forEach(tag => {
|
||||
applyBlockStyles(
|
||||
tag,
|
||||
template.options.block?.[tag],
|
||||
styleOptions.block?.[tag]
|
||||
)
|
||||
})
|
||||
|
||||
// 应用段落样式
|
||||
applyBlockStyles(
|
||||
'p',
|
||||
template.options.block?.p,
|
||||
styleOptions.block?.p
|
||||
)
|
||||
|
||||
// 应用引用样式
|
||||
applyBlockStyles(
|
||||
'blockquote',
|
||||
template.options.block?.blockquote,
|
||||
styleOptions.block?.blockquote
|
||||
)
|
||||
|
||||
// 应用代码块样式
|
||||
applyBlockStyles(
|
||||
'pre',
|
||||
template.options.block?.code_pre,
|
||||
styleOptions.block?.code_pre
|
||||
)
|
||||
|
||||
// 应用图片样式
|
||||
applyBlockStyles(
|
||||
'img',
|
||||
template.options.block?.image,
|
||||
styleOptions.block?.image
|
||||
)
|
||||
|
||||
// 应用列表样式
|
||||
applyBlockStyles(
|
||||
'ul',
|
||||
template.options.block?.ul,
|
||||
styleOptions.block?.ul
|
||||
)
|
||||
applyBlockStyles(
|
||||
'ol',
|
||||
template.options.block?.ol,
|
||||
styleOptions.block?.ol
|
||||
)
|
||||
|
||||
// 应用内联样式
|
||||
const applyInlineStyles = (selector: string, templateStyles: any, customStyles: any) => {
|
||||
styleContainer.querySelectorAll(selector).forEach(element => {
|
||||
const effectiveStyles: Record<string, string> = {}
|
||||
|
||||
// 应用模板样式
|
||||
if (templateStyles) {
|
||||
Object.entries(templateStyles).forEach(([key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
effectiveStyles[key] = replaceCSSVariables(value)
|
||||
} else {
|
||||
effectiveStyles[key] = value as string
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 应用自定义样式
|
||||
if (customStyles) {
|
||||
Object.entries(customStyles).forEach(([key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
effectiveStyles[key] = replaceCSSVariables(value)
|
||||
} else {
|
||||
effectiveStyles[key] = value as string
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Object.assign((element as HTMLElement).style, effectiveStyles)
|
||||
})
|
||||
}
|
||||
|
||||
// 应用加粗样式
|
||||
applyInlineStyles(
|
||||
'strong',
|
||||
template.options.inline?.strong,
|
||||
styleOptions.inline?.strong
|
||||
)
|
||||
|
||||
// 应用斜体样式
|
||||
applyInlineStyles(
|
||||
'em',
|
||||
template.options.inline?.em,
|
||||
styleOptions.inline?.em
|
||||
)
|
||||
|
||||
// 应用行内代码样式
|
||||
applyInlineStyles(
|
||||
'code:not(pre code)',
|
||||
template.options.inline?.codespan,
|
||||
styleOptions.inline?.codespan
|
||||
)
|
||||
|
||||
// 应用链接样式
|
||||
applyInlineStyles(
|
||||
'a',
|
||||
template.options.inline?.link,
|
||||
styleOptions.inline?.link
|
||||
)
|
||||
|
||||
// 在应用所有样式后,确保基础样式被正确应用
|
||||
applyBaseStyles()
|
||||
|
||||
// 处理 CSS 变量
|
||||
const cssVariables = {
|
||||
'--md-primary-color': styleOptions.base?.primaryColor || template?.options.base?.primaryColor || '#333333',
|
||||
'--foreground': styleOptions.base?.themeColor || template?.options.base?.themeColor || '#1a1a1a',
|
||||
'--background': '#ffffff',
|
||||
'--muted': '#f1f5f9',
|
||||
'--muted-foreground': '#64748b',
|
||||
'--blockquote-background': 'rgba(0, 0, 0, 0.05)'
|
||||
}
|
||||
|
||||
const htmlBlob = new Blob([tempDiv.innerHTML], { type: 'text/html' })
|
||||
const textBlob = new Blob([tempDiv.innerText], { type: 'text/plain' })
|
||||
// 替换所有元素中的 CSS 变量
|
||||
const replaceVariables = (element: HTMLElement) => {
|
||||
const style = window.getComputedStyle(element)
|
||||
const properties = ['color', 'background-color', 'border-color', 'border-left-color']
|
||||
|
||||
properties.forEach(prop => {
|
||||
const value = style.getPropertyValue(prop)
|
||||
if (value.includes('var(')) {
|
||||
let finalValue = value
|
||||
Object.entries(cssVariables).forEach(([variable, replacement]) => {
|
||||
finalValue = finalValue.replace(`var(${variable})`, replacement)
|
||||
})
|
||||
element.style[prop as any] = finalValue
|
||||
}
|
||||
})
|
||||
|
||||
// 递归处理子元素
|
||||
Array.from(element.children).forEach(child => {
|
||||
if (child instanceof HTMLElement) {
|
||||
replaceVariables(child)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 处理所有元素
|
||||
Array.from(tempDiv.children).forEach(child => {
|
||||
if (child instanceof HTMLElement) {
|
||||
replaceVariables(child)
|
||||
}
|
||||
})
|
||||
|
||||
// 创建并写入剪贴板
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
'text/html': htmlBlob,
|
||||
'text/plain': textBlob
|
||||
'text/html': new Blob([tempDiv.innerHTML], { type: 'text/html' }),
|
||||
'text/plain': new Blob([tempDiv.innerText], { type: 'text/plain' })
|
||||
})
|
||||
])
|
||||
|
||||
toast({
|
||||
title: "复制成功",
|
||||
description: template
|
||||
? "已复制预览内容(包含样式)"
|
||||
: "已复制预览内容",
|
||||
description: "已复制预览内容",
|
||||
duration: 2000
|
||||
})
|
||||
} catch (err) {
|
||||
|
@ -52,6 +52,7 @@ export const templates: Template[] = [
|
||||
lineHeight: 1.2
|
||||
},
|
||||
p: {
|
||||
fontSize:'12px',
|
||||
margin: '1.5em 8px',
|
||||
letterSpacing: '0.1em',
|
||||
color: 'hsl(var(--foreground))',
|
||||
|
Loading…
Reference in New Issue
Block a user