Loading...
Loading...
Compare original and translation side by side
unlayer.init()unlayer.init()unlayer.init({
features: {
audit: true, // Content validation
preview: true, // Preview button
undoRedo: true, // Undo/redo
stockImages: true, // Stock photo library
userUploads: true, // User upload tab
preheaderText: true, // Email preheader field
textEditor: {
spellChecker: true,
tables: false, // Tables inside text blocks
cleanPaste: 'confirm', // true | false | 'basic' | 'confirm'
emojis: true,
},
// Paid features:
ai: true, // AI text generation
collaboration: false, // Real-time collaboration
sendTestEmail: false, // Test email button
},
});unlayer.init({
features: {
audit: true, // 内容验证
preview: true, // 预览按钮
undoRedo: true, // 撤销/重做
stockImages: true, // 库存图片库
userUploads: true, // 用户上传标签页
preheaderText: true, // 邮件预头字段
textEditor: {
spellChecker: true,
tables: false, // 文本块内的表格
cleanPaste: 'confirm', // true | false | 'basic' | 'confirm'
emojis: true,
},
// 付费功能:
ai: true, // AI文本生成
collaboration: false, // 实时协作
sendTestEmail: false, // 测试邮件按钮
},
});unlayer.init({
appearance: {
theme: 'modern_dark', // 'modern_light' | 'modern_dark' | 'classic_light' | 'classic_dark'
panels: {
tools: {
dock: 'right', // 'left' | 'right' (default: 'right')
collapsible: true,
},
},
actionBar: {
placement: 'top', // 'top' | 'bottom' | 'top_left' | 'top_right' | 'bottom_left' | 'bottom_right'
},
},
});
// Change at runtime:
unlayer.setAppearance({ theme: 'modern_dark' });
// Or just the theme:
unlayer.setTheme('modern_dark');unlayer.init({
appearance: {
theme: 'modern_dark', // 'modern_light' | 'modern_dark' | 'classic_light' | 'classic_dark'
panels: {
tools: {
dock: 'right', // 'left' | 'right'(默认值:'right')
collapsible: true,
},
},
actionBar: {
placement: 'top', // 'top' | 'bottom' | 'top_left' | 'top_right' | 'bottom_left' | 'bottom_right'
},
},
});
// 运行时更改:
unlayer.setAppearance({ theme: 'modern_dark' });
// 或仅更改主题:
unlayer.setTheme('modern_dark');unlayer.init({
fonts: {
showDefaultFonts: true,
customFonts: [{
label: 'Poppins',
value: "'Poppins', sans-serif",
url: 'https://fonts.googleapis.com/css?family=Poppins:400,700',
weights: [400, 700], // or [{ label: 'Regular', value: 400 }, { label: 'Bold', value: 700 }]
}],
},
});unlayer.init({
fonts: {
showDefaultFonts: true,
customFonts: [{
label: 'Poppins',
value: "'Poppins', sans-serif",
url: 'https://fonts.googleapis.com/css?family=Poppins:400,700',
weights: [400, 700], // 或 [{ label: 'Regular', value: 400 }, { label: 'Bold', value: 700 }]
}],
},
});unlayer.init({
tabs: {
content: { enabled: true, position: 1 },
blocks: { enabled: true, position: 2 },
body: { enabled: true, position: 3 },
images: { enabled: true, position: 4 },
uploads: { enabled: true, position: 5 },
},
});unlayer.init({
tabs: {
content: { enabled: true, position: 1 },
blocks: { enabled: true, position: 2 },
body: { enabled: true, position: 3 },
images: { enabled: true, position: 4 },
uploads: { enabled: true, position: 5 },
},
});{{first_name}}unlayer.setMergeTags({
first_name: {
name: 'First Name',
value: '{{first_name}}', // Your template syntax
sample: 'John', // Shown in editor preview
},
last_name: {
name: 'Last Name',
value: '{{last_name}}',
sample: 'Doe',
},
company: {
name: 'Company', // Nested group
mergeTags: {
name: { name: 'Company Name', value: '{{company.name}}', sample: 'Acme Inc' },
logo: { name: 'Logo URL', value: '{{company.logo}}' },
},
},
products: {
name: 'Products',
rules: {
repeat: {
name: 'Repeat for Each Product',
before: '{{#each products}}', // Loop start — syntax depends on your template engine
after: '{{/each}}', // Loop end
sample: true, // Show sample data in editor
},
},
mergeTags: {
name: { name: 'Product Name', value: '{{this.name}}' },
price: { name: 'Price', value: '{{this.price}}' },
image: { name: 'Image URL', value: '{{this.image}}' },
},
},
});
// Autocomplete trigger (optional)
unlayer.setMergeTagsConfig({ autocompleteTriggerChar: '{{', sort: true });{{first_name}}unlayer.setMergeTags({
first_name: {
name: 'First Name',
value: '{{first_name}}', // 你的模板语法
sample: 'John', // 编辑器预览中显示的示例
},
last_name: {
name: 'Last Name',
value: '{{last_name}}',
sample: 'Doe',
},
company: {
name: 'Company', // 嵌套组
mergeTags: {
name: { name: 'Company Name', value: '{{company.name}}', sample: 'Acme Inc' },
logo: { name: 'Logo URL', value: '{{company.logo}}' },
},
},
products: {
name: 'Products',
rules: {
repeat: {
name: 'Repeat for Each Product',
before: '{{#each products}}', // 循环开始——语法取决于你的模板引擎
after: '{{/each}}', // 循环结束
sample: true, // 在编辑器中显示示例数据
},
},
mergeTags: {
name: { name: 'Product Name', value: '{{this.name}}' },
price: { name: 'Price', value: '{{this.price}}' },
image: { name: 'Image URL', value: '{{this.image}}' },
},
},
});
// 自动补全触发(可选)
unlayer.setMergeTagsConfig({ autocompleteTriggerChar: '{{', sort: true });unlayer.setDesignTags({
business_name: 'Acme Corp',
current_user_name: 'Jane Smith',
});
unlayer.setDesignTagsConfig({ delimiter: ['{{', '}}'] });unlayer.setDesignTags({
business_name: 'Acme Corp',
current_user_name: 'Jane Smith',
});
unlayer.setDesignTagsConfig({ delimiter: ['{{', '}}'] });unlayer.setDisplayConditions([
{
type: 'segment',
label: 'VIP Customers',
description: 'Only shown to VIP segment',
before: '{% if customer.vip %}', // Your template engine syntax
after: '{% endif %}',
},
{
type: 'segment',
label: 'New Subscribers',
description: 'First 30 days only',
before: '{% if subscriber.age_days < 30 %}',
after: '{% endif %}',
},
]);unlayer.setDisplayConditions([
{
type: 'segment',
label: 'VIP Customers',
description: '仅对VIP用户组显示',
before: '{% if customer.vip %}', // 你的模板引擎语法
after: '{% endif %}',
},
{
type: 'segment',
label: '新订阅用户',
description: '仅显示给订阅30天内的用户',
before: '{% if subscriber.age_days < 30 %}',
after: '{% endif %}',
},
]);unlayer.setSpecialLinks({
unsubscribe: {
name: 'Unsubscribe',
href: '{{unsubscribe_url}}',
target: '_blank',
},
preferences: {
name: 'Preferences',
specialLinks: {
email_prefs: { name: 'Email Preferences', href: '{{preferences_url}}' },
profile: { name: 'Profile Settings', href: '{{profile_url}}' },
},
},
});unlayer.setSpecialLinks({
unsubscribe: {
name: '退订',
href: '{{unsubscribe_url}}',
target: '_blank',
},
preferences: {
name: '偏好设置',
specialLinks: {
email_prefs: { name: '邮件偏好', href: '{{preferences_url}}' },
profile: { name: '个人资料设置', href: '{{profile_url}}' },
},
},
});const crypto = require('crypto');
const signature = crypto
.createHmac('sha256', 'YOUR_PROJECT_SECRET') // From Dashboard
.update(String(userId))
.digest('hex');import hmac, hashlib
signature = hmac.new(
b'YOUR_PROJECT_SECRET',
bytes(str(request.user.id), encoding='utf-8'),
digestmod=hashlib.sha256
).hexdigest()unlayer.init({
user: {
id: userId, // Must match what you signed
signature: signatureFromServer, // HMAC from your backend
name: 'John Doe', // Optional
email: 'john@acme.com', // Optional
},
});const crypto = require('crypto');
const signature = crypto
.createHmac('sha256', 'YOUR_PROJECT_SECRET') // 来自控制台
.update(String(userId))
.digest('hex');import hmac, hashlib
signature = hmac.new(
b'YOUR_PROJECT_SECRET',
bytes(str(request.user.id), encoding='utf-8'),
digestmod=hashlib.sha256
).hexdigest()unlayer.init({
user: {
id: userId, // 必须与你签名的ID一致
signature: signatureFromServer, // 来自后端的HMAC
name: 'John Doe', // 可选
email: 'john@acme.com', // 可选
},
});unlayer.registerCallback('image', (file, done) => {
const data = new FormData();
data.append('file', file.attachments[0]);
fetch('/api/uploads', { method: 'POST', body: data })
.then((r) => {
if (!r.ok) throw new Error('Upload failed');
return r.json();
})
.then((result) => done({ progress: 100, url: result.url }))
.catch((err) => console.error('Upload error:', err));
});{ "url": "https://your-cdn.com/images/uploaded-file.png" }unlayer.registerCallback('image', (file, done) => {
const data = new FormData();
data.append('file', file.attachments[0]);
fetch('/api/uploads', { method: 'POST', body: data })
.then((r) => {
if (!r.ok) throw new Error('上传失败');
return r.json();
})
.then((result) => done({ progress: 100, url: result.url }))
.catch((err) => console.error('上传错误:', err));
});{ "url": "https://your-cdn.com/images/uploaded-file.png" }user.idunlayer.init({
user: { id: 123 }, // Required for file manager
features: { userUploads: { enabled: true, search: true } },
});
unlayer.registerProvider('userUploads', (params, done) => {
// params: { page, perPage, searchText }
fetch(`/api/images?userId=123&page=${params.page}&perPage=${params.perPage}`)
.then((r) => r.json())
.then((data) => {
done(
data.items.map((img) => ({
id: img.id, // Required
location: img.url, // Required — the image URL
width: img.width, // Optional but recommended
height: img.height, // Optional but recommended
contentType: img.contentType, // Optional: 'image/png'
source: 'user', // Required: must be 'user'
})),
{ hasMore: data.hasMore, page: params.page, total: data.total }
);
});
});{
"items": [
{ "id": "img_1", "url": "https://...", "width": 800, "height": 600, "contentType": "image/png" }
],
"hasMore": true,
"total": 42
}user.idunlayer.init({
user: { id: 123 }, // 文件管理器必需
features: { userUploads: { enabled: true, search: true } },
});
unlayer.registerProvider('userUploads', (params, done) => {
// params: { page, perPage, searchText }
fetch(`/api/images?userId=123&page=${params.page}&perPage=${params.perPage}`)
.then((r) => r.json())
.then((data) => {
done(
data.items.map((img) => ({
id: img.id, // 必需
location: img.url, // 必需——图片URL
width: img.width, // 可选但推荐
height: img.height, // 可选但推荐
contentType: img.contentType, // 可选: 'image/png'
source: 'user', // 必需: 必须为'user'
})),
{ hasMore: data.hasMore, page: params.page, total: data.total }
);
});
});{
"items": [
{ "id": "img_1", "url": "https://...", "width": 800, "height": 600, "contentType": "image/png" }
],
"hasMore": true,
"total": 42
}unlayer.init({
locale: 'es-ES',
textDirection: 'rtl', // 'ltr' | 'rtl' | null
translations: {
es: { Save: 'Guardar', Cancel: 'Cancelar' },
},
});unlayer.init({
locale: 'es-ES',
textDirection: 'rtl', // 'ltr' | 'rtl' | null
translations: {
es: { Save: 'Guardar', Cancel: 'Cancelar' },
},
});// Global validator — runs on all content
unlayer.setValidator(async ({ html, design, defaultErrors }) => {
return [...defaultErrors]; // Return modified error list
});
// Per-tool validator
unlayer.setToolValidator('text', async ({ html, defaultErrors }) => {
return defaultErrors;
});
// Run audit on demand
unlayer.audit((result) => {
// result: { status: 'FAIL' | 'PASS', errors: [{ id, icon, severity, title, description }] }
if (result.status === 'FAIL') {
console.log('Issues found:', result.errors);
}
});// 全局验证器——对所有内容生效
unlayer.setValidator(async ({ html, design, defaultErrors }) => {
return [...defaultErrors]; // 返回修改后的错误列表
});
// 按工具验证
unlayer.setToolValidator('text', async ({ html, defaultErrors }) => {
return defaultErrors;
});
// 按需运行审核
unlayer.audit((result) => {
// result: { status: 'FAIL' | 'PASS', errors: [{ id, icon, severity, title, description }] }
if (result.status === 'FAIL') {
console.log('发现问题:', result.errors);
}
});unlayer.init({
safeHtml: true, // Sanitize HTML via DOMPurify
// Or with custom options:
safeHtml: {
domPurifyOptions: {
FORCE_BODY: true,
},
},
// WRONG: safeHTML (capital HTML) is DEPRECATED — use safeHtml
});unlayer.init({
safeHtml: true, // 通过DOMPurify清理HTML
// 或使用自定义选项:
safeHtml: {
domPurifyOptions: {
FORCE_BODY: true,
},
},
// 错误写法: safeHTML(大写HTML)已被弃用——请使用safeHtml
});| Mistake | Fix |
|---|---|
| Use |
| It disables blocks but NOT the tab — use |
Deprecated | Use |
Missing | File Manager requires |
| Project Secret exposed in frontend | Never put the secret in client code — generate HMAC server-side |
| Merge tag syntax mismatch | Match your template engine: |
| 错误 | 修复方案 |
|---|---|
| 使用 |
| 它会禁用块但不会隐藏标签页——请使用 |
已弃用的 | 改用 |
文件管理器缺少 | 文件管理器需要在init中传入 |
| 项目密钥暴露在前端 | 绝对不要在客户端代码中放入密钥——在服务端生成HMAC |
| Merge Tags语法不匹配 | 匹配你的模板引擎: |
| Problem | Fix |
|---|---|
| Merge tags don't appear | Check |
| HMAC signature rejected | Ensure |
| File manager shows empty | Check |
| Theme doesn't apply | Use |
| 问题 | 修复方案 |
|---|---|
| Merge Tags不显示 | 确保 |
| HMAC签名被拒绝 | 确保 |
| 文件管理器显示为空 | 检查 |
| 主题未生效 | 使用 |
| Feature | How to Enable |
|---|---|
| Custom CSS/JS | |
| Display conditions | |
| Style guide | |
| Export Image/PDF/ZIP | Cloud API key required |
| AI features | |
| Collaboration | |
| 功能 | 启用方式 |
|---|---|
| 自定义CSS/JS | 在init中传入 |
| 显示条件 | 使用 |
| 样式指南 | 使用 |
| 导出图片/PDF/ZIP | 需要云API密钥 |
| AI功能 | 设置 |
| 协作功能 | 设置 |