千千块模板 | 自定义块 css+js 一键注入到目标块(商业化必备模版)
开发自定义块 css+js 大型项目最后用 webpack 打包生成的 dist 文件夹里的 css 和 js 文件都很大,不方便复制粘贴
则需要这个模版一键注入到目标块属性里面
功能:一键注入到目标块 custom-css 和 custom-js 属性里面(可以实现不用复制粘贴就能用上自定义 css+js)
选择指定块,最左侧的块图标按一下——复制——复制 ID

自定义块 js
// --- 初始化界面 ---
this.innerHTML = '';
this.style.cssText = 'display: flex; flex-direction: column; gap: 12px; padding: 16px; border: 1px solid #d1d5db; border-radius: 8px; background-color: #f9fafb;';
let jsFileContent = null;
let cssFileContent = null;
// --- 创建文件上传区域 (JS) ---
const jsContainer = document.createElement('div');
jsContainer.style.cssText = 'display: flex; align-items: center; gap: 8px;';
const jsLabel = document.createElement('label');
jsLabel.textContent = '1. 上传 JavaScript 文件 (.js)';
jsLabel.style.fontWeight = 'bold';
const jsFileInput = document.createElement('input');
jsFileInput.type = 'file';
jsFileInput.accept = '.js';
jsFileInput.style.display = 'none';
const jsButton = document.createElement('button');
jsButton.textContent = '选择文件';
const jsButtonClickHandler = () => jsFileInput.click();
jsButton.onclick = jsButtonClickHandler;
styleButton(jsButton);
const jsFileDisplay = document.createElement('span');
jsFileDisplay.textContent = '未选择文件';
jsFileDisplay.style.color = '#6b7280';
jsFileDisplay.style.flexGrow = '1';
const jsDeleteButton = document.createElement('button');
jsDeleteButton.textContent = '❌';
styleDeleteButton(jsDeleteButton);
jsDeleteButton.style.display = 'none'; // 默认隐藏
jsContainer.append(jsLabel, jsButton, jsFileDisplay, jsDeleteButton);
// --- 创建文件上传区域 (CSS) ---
const cssContainer = document.createElement('div');
cssContainer.style.cssText = 'display: flex; align-items: center; gap: 8px;';
const cssLabel = document.createElement('label');
cssLabel.textContent = '2. 上传 CSS 文件 (.css)';
cssLabel.style.fontWeight = 'bold';
const cssFileInput = document.createElement('input');
cssFileInput.type = 'file';
cssFileInput.accept = '.css';
cssFileInput.style.display = 'none';
const cssButton = document.createElement('button');
cssButton.textContent = '选择文件';
const cssButtonClickHandler = () => cssFileInput.click();
cssButton.onclick = cssButtonClickHandler;
styleButton(cssButton);
const cssFileDisplay = document.createElement('span');
cssFileDisplay.textContent = '未选择文件';
cssFileDisplay.style.color = '#6b7280';
cssFileDisplay.style.flexGrow = '1';
const cssDeleteButton = document.createElement('button');
cssDeleteButton.textContent = '❌';
styleDeleteButton(cssDeleteButton);
cssDeleteButton.style.display = 'none'; // 默认隐藏
cssContainer.append(cssLabel, cssButton, cssFileDisplay, cssDeleteButton);
// --- 创建块ID输入区域 ---
const idContainer = document.createElement('div');
idContainer.style.cssText = 'display: flex; align-items: center; gap: 8px;';
const idLabel = document.createElement('label');
idLabel.textContent = '3. 目标块 ID';
idLabel.style.fontWeight = 'bold';
const idInput = document.createElement('input');
idInput.type = 'text';
idInput.placeholder = '例如: 20230101120000-abcdefg';
idInput.style.cssText = 'flex-grow: 1; padding: 8px; border: 1px solid #d1d5db; border-radius: 4px;';
idContainer.append(idLabel, idInput);
// --- 创建开始按钮 ---
const startButton = document.createElement('button');
startButton.textContent = '🚀 开始更新';
startButton.style.cssText = 'padding: 10px 15px; border: none; border-radius: 6px; background-color: #2563eb; color: white; font-weight: bold; cursor: pointer; transition: background-color 0.2s;';
startButton.onmouseover = () => startButton.style.backgroundColor = '#1d4ed8';
startButton.onmouseout = () => startButton.style.backgroundColor = '#2563eb';
// --- 事件处理函数 ---
const jsFileChangeHandler = (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
jsFileContent = e.target.result;
jsFileDisplay.textContent = `✅ ${file.name}`;
jsFileDisplay.style.color = '#16a34a';
jsDeleteButton.style.display = 'inline-block';
};
reader.readAsText(file);
}
};
const cssFileChangeHandler = (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
cssFileContent = e.target.result;
cssFileDisplay.textContent = `✅ ${file.name}`;
cssFileDisplay.style.color = '#16a34a';
cssDeleteButton.style.display = 'inline-block';
};
reader.readAsText(file);
}
};
const jsDeleteClickHandler = () => {
jsFileContent = null;
jsFileInput.value = ''; // 清空input的值,以便可以再次选择相同文件
jsFileDisplay.textContent = '未选择文件';
jsFileDisplay.style.color = '#6b7280';
jsDeleteButton.style.display = 'none';
};
const cssDeleteClickHandler = () => {
cssFileContent = null;
cssFileInput.value = ''; // 清空input的值
cssFileDisplay.textContent = '未选择文件';
cssFileDisplay.style.color = '#6b7280';
cssDeleteButton.style.display = 'none';
};
const startButtonClickHandler = async () => {
const blockId = idInput.value.trim();
if (!blockId) {
pushMessage('请输入目标块的ID!', 'error');
return;
}
if (!jsFileContent && !cssFileContent) {
pushMessage('请至少上传一个 JS 或 CSS 文件!', 'error');
return;
}
let existingAttrs = {};
try {
const res = await fetch('/api/attr/getBlockAttrs', {
method: 'POST',
body: JSON.stringify({ id: blockId })
});
const result = await res.json();
if (result.code === 0) {
existingAttrs = result.data;
} else {
pushMessage(`获取块属性失败: ${result.msg}`, 'error');
return;
}
} catch (e) {
pushMessage(`获取块属性时发生网络错误: ${e}`, 'error');
return;
}
if (!jsFileContent) {
delete existingAttrs['custom-js'];
} else {
existingAttrs['custom-js'] = jsFileContent;
}
if (!cssFileContent) {
delete existingAttrs['custom-css'];
} else {
existingAttrs['custom-css'] = cssFileContent;
}
try {
const response = await fetch('/api/attr/setBlockAttrs', {
method: 'POST',
body: JSON.stringify({
id: blockId,
attrs: existingAttrs
})
});
const result = await response.json();
if (result.code === 0) {
let updatedFiles = [];
if (jsFileContent) updatedFiles.push('JS');
if (cssFileContent) updatedFiles.push('CSS');
let successMsg = `成功为块 ${blockId} 更新了 ${updatedFiles.join(' 和 ')} 属性!`;
if (updatedFiles.length === 0) {
successMsg = `成功为块 ${blockId} 清除了自定义属性!`;
}
pushMessage(successMsg);
} else {
pushMessage(`更新失败: ${result.msg}`, 'error');
}
} catch (error) {
pushMessage(`发生网络错误: ${error}`, 'error');
}
};
// --- 绑定事件监听 ---
jsFileInput.addEventListener('change', jsFileChangeHandler);
cssFileInput.addEventListener('change', cssFileChangeHandler);
jsDeleteButton.addEventListener('click', jsDeleteClickHandler);
cssDeleteButton.addEventListener('click', cssDeleteClickHandler);
startButton.addEventListener('click', startButtonClickHandler);
// --- 辅助函数 ---
function pushMessage(msg, type = 'info', timeout = 3000) {
const api = type === 'error' ? '/api/notification/pushErrMsg' : '/api/notification/pushMsg';
fetch(api, {
method: 'POST',
body: JSON.stringify({ msg, timeout })
});
}
function styleButton(button) {
button.style.cssText = 'padding: 6px 12px; border: 1px solid #d1d5db; border-radius: 6px; background-color: white; cursor: pointer; transition: background-color 0.2s;';
button.onmouseover = () => button.style.backgroundColor = '#f3f4f6';
button.onmouseout = () => button.style.backgroundColor = 'white';
}
function styleDeleteButton(button) {
button.style.cssText = 'padding: 6px; border: none; background-color: transparent; cursor: pointer; font-size: 14px; line-height: 1; border-radius: 50%;';
button.onmouseover = () => button.style.backgroundColor = '#e5e7eb';
button.onmouseout = () => button.style.backgroundColor = 'transparent';
}
// --- 将所有元素添加到块中 ---
this.append(jsContainer, jsFileInput, cssContainer, cssFileInput, idContainer, startButton);
// --- 返回清理函数 ---
return () => {
jsFileInput.removeEventListener('change', jsFileChangeHandler);
cssFileInput.removeEventListener('change', cssFileChangeHandler);
jsDeleteButton.removeEventListener('click', jsDeleteClickHandler);
cssDeleteButton.removeEventListener('click', cssDeleteClickHandler);
startButton.removeEventListener('click', startButtonClickHandler);
jsButton.onclick = null;
cssButton.onclick = null;
};
商业化必备模版,将这个模版给你的用户使用,方便大型项目不易复制粘贴内容。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于