var faCogActive = true; var faThLargeActive = false; var activeIcon = getVariable('active_icon'); if (activeIcon) { faCogActive = activeIcon === 'fa-cog'? true : false; faThLargeActive = activeIcon === 'fa-th-large'? true : false; } const panelsConfig = { stylePrefix: 'pn-', // 面板的样式前缀 defaults: [ { id: 'commands', buttons: [{}], }, { id: 'options', buttons: [ { active: false, id: 'sw-visibility', className: 'fa fa-square-o', command: 'core:component-outline', context: 'sw-visibility', attributes: { title: 'View components' }, }, { id: 'preview', className: 'fa fa-eye', command: 'preview', context: 'preview', attributes: { title: 'Preview' }, }, { id: 'fullscreen', className: 'fa fa-arrows-alt', command: 'fullscreen', context: 'fullscreen', attributes: { title: 'Fullscreen' }, }, // { // id: 'export-template', // className: 'fa fa-code', // command: 'export-template', // attributes: { title: 'View code' }, // }, ], }, { id: 'views', buttons: [ // { // id: 'open-sm', // className: 'fa fa-paint-brush', // command: 'open-sm', // active: false, // togglable: false, // attributes: { title: 'Open Style Manager' }, // }, { id: 'open-tm', className: 'fa fa-cog', command: 'open-tm', active: faCogActive, togglable: true, attributes: { title: 'Settings' }, }, // { // id: 'open-layers', // className: 'fa fa-bars', // command: 'open-layers', // togglable: false, // attributes: { title: 'Open Layer Manager' }, // }, // { // id: 'open-blocks', // className: 'fa fa-th-large', // command: 'open-blocks', // active: faThLargeActive, // togglable: true, // attributes: { title: 'Open Blocks' }, // }, ], }, ], }; // 定义自定义组件类型 const myNewComponentTypes = (editor) => { //连接属性 // editor.Components.addType('a', { // isComponent: (el) => el.tagName === 'A', // model: { // defaults: { // traits: [ // { // type: 'text', // Type of the trait // name: 'href', // (required) The name of the attribute/property to use on component // label: 'URL', // The label you will see in Settings // } // ], // }, // }, // }); //图片属性 // editor.Components.addType('img', { // isComponent: (el) => el.tagName === 'IMG', // model: { // defaults: { // traits: [ // { // type: 'text', // Type of the trait // name: 'title', // (required) The name of the attribute/property to use on component // label: 'title', // The label you will see in Settings // }, // { // type: 'text', // Type of the trait // name: 'width', // (required) The name of the attribute/property to use on component // label: 'width', // The label you will see in Settings // }, // { // type: 'text', // Type of the trait // name: 'height', // (required) The name of the attribute/property to use on component // label: 'height', // The label you will see in Settings // }, // ], // }, // }, // }); }; const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); // 创建 GrapesJS 编辑器实例 const editor = grapesjs.init({ container: '#mtb_visual_editor', // 指定编辑器容器的 DOM 元素 fromElement: true, // 从 DOM 元素中加载初始内容 height: '100%', // 设置编辑器的高度 width: '100%', // 设置宽度自适应 storageManager: false, panels: panelsConfig, plugins: [myNewComponentTypes], assetManager: { upload: '/dist/visual-editor/upload', uploadName: '_file_', multiUpload:false, headers: { 'X-CSRF-TOKEN': csrfToken, }, }, }); //定义block块 const bm = editor.Blocks; // `Blocks` is an alias of `BlockManager` bm.add('block_p', { // Your block properties... label: 'Paragraph', media: ``, content: '
insert your paragraph here
', }); bm.add('block_span', { // Your block properties... label: 'Text', media: ``, content: 'insert your text here', }); //图片管理器 const am = editor.AssetManager; // 锁定整个画布 editor.getWrapper().set({ locked: true }); // 遍历所有组件并解锁具有edit属性的组件 // const edit_classes = ['mtb_edit']; // 定义需要解锁的组件类名 editor.getComponents().each(component => { component.onAll(component => { //解销CLASS // let html = component.toHTML(); // const classes = component.getClasses(); // // 判断是否有交集 // const hasIntersection = classes.some(item => { // // 如果是数组,则递归检查子数组 // if (Array.isArray(item)) { // return item.some(subItem => edit_classes.includes(subItem)); // } // // 如果是字符串,直接检查是否在 edit_classes 中 // return edit_classes.includes(item); // }); // 如果有交集,解锁组件 if (component.get('attributes').mtb_edit == '1' || component.get('attributes').mtb_edit == 'true') { component.set({ locked: false }); setBorder(component); } if (component.get('attributes').mtb_edit == '0' || component.get('attributes').mtb_edit == 'false') { component.set({ locked: true }); } if (component.get('attributes').mtb_edit == '2') { component.set({ locked: false }); setBorder(component); //关闭子组件 component.forEachChild(child => { child.set({ locked: true }); }) } }) }); function setBorder(component) { if (component.attributes.tagName == 'a') { component.setStyle({ border: '2px dotted #f08300' }); } else { component.setStyle({ border: '1px dotted #3b97e3' }); } } // 监听组件选择事件 editor.on('component:selected', (comp) => { //去掉选中组件后的按钮 const selectedComponent = editor.getSelected(); //const defaultToolbar = selectedComponent.get('toolbar'); //先不给删除按钮 if (selectedComponent.get('attributes').mtb_edit == 'true' || selectedComponent.get('attributes').mtb_edit == '1' || selectedComponent.get('attributes').mtb_edit == '2') { selectedComponent.set({ toolbar: [ ] }); } else { selectedComponent.set({ toolbar: [ ] // toolbar: [ ...defaultToolbar, { attributes: {class: commandIcon}, command: commandToAdd}] // toolbar: [{ // attributes: { class: 'fa fa-clone' }, // command: 'tlb-clone', // title: 'Clone', // }, { // attributes: { class: 'fa fa-trash' }, // command: 'tlb-delete', // title: 'Delete', // }] }); } if (selectedComponent.get('attributes').mtb_toolbar != '' && selectedComponent.get('attributes').mtb_toolbar != undefined) { const mtbToolbar = selectedComponent.get('attributes').mtb_toolbar; const safeActions = mtbToolbar.split(','); console.log(safeActions); let inputToolbar = []; if (safeActions.includes('clone')) { // 执行克隆相关逻辑 inputToolbar.push({ attributes: { class: 'fa fa-clone' }, command: 'tlb-clone', title: 'Clone', }); } if (safeActions.includes('delete')) { // 执行删除相关逻辑 inputToolbar.push({ attributes: { class: 'fa fa-trash' }, command: 'tlb-delete', title: 'Delete', }); } selectedComponent.set({ toolbar: inputToolbar }); } // 双击图片时弹出图片管理器 if (selectedComponent.attributes.tagName == 'img') { //双击图片时弹出图片管理器 const el = selectedComponent.getEl(); // 添加双击事件监听器 el.addEventListener('dblclick', () => { am.open({ types: ['image'], // This is the default option select(asset, complete) { const selected = editor.getSelected(); if (selected) { selected.addAttributes({ src: asset.getSrc() }); complete && am.close(); } }, }); }); } //其他 }); // 隐藏或显示左侧面板 function leftPanelDisplay(isShow = true) { if (isShow) { let elements1 = document.querySelectorAll('.gjs-pn-views-container'); elements1.forEach(function(element) { element.style.display = 'block'; // 或者 'flex',取决于你希望如何显示这些元素 }); let elements2 = document.querySelectorAll('.gjs-pn-views'); elements2.forEach(function(element) { element.style = 'border_bottom: 2px solid var(--gjs-main-dark-color)'; }); } else { let elements1 = document.querySelectorAll('.gjs-pn-views-container'); elements1.forEach(function(element) { element.style.display = 'none'; // 或者 'flex',取决于你希望如何显示这些元素 }); let elements2 = document.querySelectorAll('.gjs-pn-views'); elements2.forEach(function(element) { element.style = 'border-bottom:none'; }); } } // 监听编辑器加载完成事件 editor.on('load', () => { //---------- // 下接菜单 //---------- // 获取目标元素 const targetElement = document.querySelector('.fa-arrows-alt'); // 创建下拉选择框 const dropdown = document.createElement('select'); dropdown.className = 'custom-select'; // 添加自定义样式类 dropdown.id = 'dropdown'; // 添加 id 属性 // 构建 option 标签 var options = ''; siteMenu.forEach(function (item) { if (mid == item.id) { options += ``; } else { options += ``; } }); // 将生成的 option 标签添加到 dropdown 中 dropdown.innerHTML = options; // 将下拉选择框插入到目标元素之后 targetElement.insertAdjacentElement('afterend', dropdown); dropdownel = document.getElementById('dropdown'); // 监听 change 事件 dropdownel.addEventListener('change', function () { // 获取选中的 option var selectedOption = dropdownel.options[dropdownel.selectedIndex]; // 获取选中的 id 和 uri var id = selectedOption.value; var uri = selectedOption.getAttribute('uri'); if (id > 0) { // 生成跳转的 URL var redirectUrl = `?mid=${id}&uri=${encodeURIComponent(uri)}`; // 跳转到生成的 URL window.location.href = redirectUrl; } }); //---------- //发布 //---------- customSelect = document.querySelector('.custom-select'); // 创建 sendButton 按钮 const sendButton = document.createElement('button'); sendButton.textContent = publishBtnName; // 按钮文本 sendButton.classList.add('send-button'); sendButton.classList.add('layui-btn'); sendButton.classList.add('layui-btn-radius'); sendButton.classList.add('layui-btn-radius'); sendButton.style = 'margin-left:10px;height:25px;line-height:25px;padding:0 10px;vertical-align:unset;'; // 添加样式(可选) customSelect.insertAdjacentElement('afterend', sendButton); //发布按钮点击事件 sendButton.addEventListener('click', function () { // 显示loading loadIndex = layer.load(0, {shade: [0.5, '#000']}); // 发送数据 $.ajax({ url: '/dist/visual-editor/publish', type: 'POST', headers: { 'X-CSRF-TOKEN': csrfToken, }, data: {}, success: function (res) { layer.close(loadIndex); layer.msg(res.message,{shade: [0.5, '#000']}); }, error: function (err) { layer.close(loadIndex); layer.msg(err,{shade: [0.5, '#000']}); } }); }); //---------- // 右侧面板显示隐藏 //---------- // 监听属性按钮点击事件 var gjsPnBtn = document.querySelectorAll('.gjs-pn-btn'); var headRightBtn = ['fa-cog', 'fa-th-large']; gjsPnBtn.forEach(function(icon1) { let hasIntersection = headRightBtn.some(function(btnClass) { return icon1.classList.contains(btnClass); }); if (hasIntersection) { icon1.addEventListener('click', function() { hasActive = false; gjsPnBtn.forEach(function(icon) { if (icon.classList.contains('gjs-pn-active')) { hasActive = true; saveVariable('active_icon',icon.classList.item(2)); } }); if (hasActive) { leftPanelDisplay(true); } else { leftPanelDisplay(false); saveVariable('active_icon','none'); } }); } }); //右侧面板显示隐藏 if (faCogActive === false && faThLargeActive === false) { leftPanelDisplay(false) } //---------- //关闭loading //---------- layer.close(loadIndex); $('body').css({ visibility: 'visible', opacity: 1 }); }); //保存模版内容 editor.on('update', () => { const html = editor.getHtml(); // 你可以在这里添加自定义逻辑 //ajax提交数据 $.ajax({ url: '/dist/visual-editor/preview-save', type: 'POST', headers: { 'X-CSRF-TOKEN': csrfToken, }, data: { // 发送数据 html: html }, success: function (res) { if (res.status != 1) { layer.msg('save data error, please try again later!',{shade: [0.5, '#000']}); } }, error: function (err) { alert(err); } }); }); // 上传图片时触发loading效果 editor.on('asset:upload:start', () => { loadIndex = layer.load(0, {shade: [0.5, '#000']}); }); editor.on('asset:upload:end', () => { layer.close(loadIndex); }); // 图片懒加载 layui.use('flow', function(){ var flow = layui.flow; //当你执行这样一个方法时,即对页面中的全部带有 lay-src 的 img 元素开启了懒加载(当然你也可以指定相关 img) flow.lazyimg(); }); // 保存变量到LocalStorage function saveVariable(name, value) { localStorage.setItem(name, JSON.stringify(value)); } // 从LocalStorage读取变量 function getVariable(name) { const value = localStorage.getItem(name); return value ? JSON.parse(value) : null; }