在可视化编辑器开发中怎么做代码导入成json的

处理css:

    // 处理css
    const dealCss = () => {
      allRootColorKeys = []
      // https://developer.mozilla.org/zh-CN/docs/Web/API/CSSRule
      const cssRulesList = Array.from(document.styleSheets).find(sheet => sheet.title === 'code-preview-css')?.cssRules

      const mergeCssData = ({ itemStyle, type, oldStyles }) => {
        if (oldStyles[type]) {
          Object.assign(oldStyles[type], itemStyle)
        } else {
          oldStyles[type] = itemStyle
        }

        return oldStyles
      }

      const getMerge = ({ oldStyles, selectorText, styleMap, style }) => {
        const itemStyle = {}

        Array.from(style).forEach(key => {
          itemStyle[camelCase(key)] = style[key]
        })

        if (selectorText.includes(':')) {
          const reg = /([^:]+):(.+)/
          const selector = selectorText.match(reg)
          // console.log(selector, 'selectorselectorselector')
          return mergeCssData({ itemStyle, type: `:${selector[2]}`, oldStyles })
        } else {
          return mergeCssData({ itemStyle, type: 'normal', oldStyles })
        }
      }
      // export const pageColorKey = '--common-page-color-'

      const dealStyleRule = (rule) => {
        const { style, styleMap, selectorText } = rule

        const seletorName = selectorText.match(/((.|#)?[^:]+)/)[0]
        const seletor = $(seletorName)
        // console.log(seletorName, 'seletorseletorseletor')
        if (!seletor[0]) return

        // 处理 配色colors
        if (selectorText === ':root') {
          const colorsArr = style.cssText.split(';')
          colorsArr.forEach((item, index) => {
            const [key, val] = item.split(':')
            if (key.includes('--global-site-')) return
            pageColors[0].color_list.push({
              color_name: `模板颜色${index}`,
              color_id: `@${codePattle.rand(13)}@`,
              color_value: val.trim(),
              color_key: `--common-page-color-${randomId()}`,
              origin_key: key.trim()
            })

            allRootColorKeys.push(key.trim())
          })
        }

        const key = 'data-style'
        let oldStyles = seletor.attr(key) || '{}'
        oldStyles = JSON.parse(oldStyles)

        const dataStyle = getMerge({ oldStyles, selectorText, styleMap, style })
        seletor.attr(key, JSON.stringify(dataStyle))
      }
      Array.from(cssRulesList).forEach(rule => {
        console.log(rule, 'rulerulerule')
        if (rule.constructor === CSSStyleRule) {
          dealStyleRule(rule)
        } else if (rule.constructor === CSSMediaRule) {
          const { conditionText } = rule
          const key = 'data-media'
          Array.from(rule.cssRules).forEach(item => {
            // console.log(key, item)
            const { style, styleMap, selectorText } = item
            const seletorName = selectorText.match(/((.|#)?[^:]+)/)[0]
            let oldAllMediaStyle = $(seletorName).attr(key) || '{}'
            oldAllMediaStyle = JSON.parse(oldAllMediaStyle)
            const oldStyles = oldAllMediaStyle[conditionText] || {}

            const dataStyle = getMerge({ oldStyles, selectorText, styleMap, style })

            oldAllMediaStyle[conditionText] = dataStyle
            $(seletorName).attr(key, JSON.stringify(oldAllMediaStyle))
          })

          // const { style, styleMap, selectorText } = rule
        }
      })
      // $(selectorText).attr()

      // 'data-media': {
      //   'screen and (min-width: 800px)' : {
      //     normal: {}
      //   },
      //   'screen and (min-width: 600px)' : {
      //     normal: {}
      //   },
      // }
    }

处理js:

  // 处理js
    const dealJs = () => {
      const placeholder = '%blockID%'
      if (!newPage.javascript) return
      const blockName = newPage.javascript.match(/(?<=\/\/\s+).*?(?=\s+start)/g)
      console.log(blockName, 1111)
      if (!blockName) return
      blockName.forEach(item => {
        const rex = new RegExp(`(?<=\\/\\/\\s+${item}\\s+start)[\\s\\S]*(?=\\/\\/\\s+${item}\\s+end)`, 'g')
        const currFun = newPage.javascript.match(rex)?.[0]

        const itemFunStr = currFun.replace(/(?<=\$\(').*?(?='\))/g, (v, a) => {
          if (v.includes(item)) {
            return v.replace(item, placeholder)
          } else {
            return `${placeholder} ${v}`
          }
        })

        console.log(itemFunStr)

        $(item).attr('data-event', itemFunStr)
      })
    }

一对一处理dom

  const getDomAttrJson = (dom, type) => {
      const domStyle = dom.getAttribute(`data-${type}`) || '{}'
      return JSON.parse(domStyle) || {}
    }
// 自定义当前节点填充数据
    const matchingItemData = (newNodes, domTreeItem) => {
      if (['text', 'paragraph', 'date', 'title', 'button'].indexOf(newNodes.code) !== -1) {
        newNodes.data.text = domTreeItem.innerHTML
        newNodes.data.grade = domTreeItem.tagName.toLowerCase()
      }
    }
    // 自定义父节点数据填充
    const matchingItemPaPaData = (newNodes, domTreeItem) => {
      if (newNodes?.code && ['image', 'video', 'author-head'].includes(newNodes.code)) {
        for (let i = 0; i < domTreeItem.childNodes.length; i++) {
          if (domTreeItem.childNodes[i].src) {
            newNodes.data.url = domTreeItem.childNodes[i].src
          }
          if (domTreeItem.childNodes[i].innerHTML) {
            newNodes.data.text = domTreeItem.childNodes[i].innerHTML
          }
        }
      }
    }
    // 子节点样式数据处理
    const matchingItemStyle = (newNodes, domTreeItem) => {
      const config = editor.components.config.getComponentConfig({ group: newNodes.group, code: newNodes.code })

      newNodes.name = config.name
      const childStyle = {}
      const childMedia = {}

      codePattle.traversal(domTreeItem, itemdom => {
        const domStyle = getDomAttrJson(itemdom, 'style')
        const domMedia = getDomAttrJson(itemdom, 'media')
        for (const key in domStyle) {
          const selector = key === 'normal' ? (itemdom.classList[0] ? `.${itemdom.classList[0]}` : itemdom.nodeName.toLowerCase()) : key
          childStyle[selector] = domStyle[key]
        }
        itemdom.removeAttribute('data-style')

        // 将子节点的样式保存起来
        Object.assign(newNodes.interact, childStyle)

        for (const key in domMedia) {
          childMedia[key] = {}
          for (const k in domMedia[key]) {
            const selector = k === 'normal' ? (itemdom.classList[0] ? `.${itemdom.classList[0]}` : itemdom.nodeName.toLowerCase()) : key
            childMedia[key][selector] = domMedia[key][k]
          }

          Object.assign(newNodes.media[key], childMedia[key])
        }
        itemdom.removeAttribute('data-media')
      })
      // 加入默认属性,防止编辑属性时缺失字段报错
      console.log(newNodes.interact, 'newNodes.interactnewNodes.interact')
      newNodes.interact = { ...config.defaultData.interact, ...newNodes.interact }
    }
   // domToJson
    const domToJson = async () => {
      const { widthRange, design, gutter, show, style } = editor.state.data.pages[0]
      const newPages = {
        pages: [{
          childrenData: [],
          widthRange,
          design,
          gutter,
          show,
          style
        }]
      }
      const eachHtml = (domTree) => {
        let newNodes = {}
        const childrenData = []

        for (let i = 0; i < domTree.length; i++) {
          const currentDom = domTree[i]
          if (currentDom.nodeType !== Node.TEXT_NODE && currentDom.nodeType !== Node.COMMENT_NODE) {
            // 节点信息初始化

            const tagName = currentDom.localName.toUpperCase()
            let code = ''
            if (currentDom.nodeType === Node.ELEMENT_NODE) {
              const componentCode = currentDom?.getAttribute('data-component')
              if (componentCode) {
                code = componentCode
              } else if (/H\d/.test(tagName)) {
                code = 'title'
              } else if (tagName === 'P') {
                code = 'paragraph'
              } else if (tagName === 'PICTURE') {
                code = 'image'
              } else if (tagName === 'BUTTON') {
                code = 'button'
              } else {
                code = 'column-container'
              }
            }

            console.log(code, "currentDom?.getAttribute('data-component')")

            // const initColors = {}
            const allStyle = getDomAttrJson(currentDom, 'style')
            newNodes = {
              code,
              tagName,
              className: currentDom.className,
              childrenData: [],
              data: {},
              style: cloneDeep(allStyle.normal) || {}, // 存作为标记元素的样式
              // keys,
              group: '基本元件',
              attrs: {},
              media: getDomAttrJson(currentDom, 'media'), // 存媒体查询相关css
              interact: {} // 存伪元素和不在元素配置的子节点中的子元素样式
            }

            // 以组件/区块为单位的js脚本
            const script = currentDom?.getAttribute('data-event')
            script && (newNodes.script = script)

            console.log(newNodes.style, 'stylestylestylestyle')

            // 把当前节点交互相关的css 也保存起来
            delete allStyle.normal
            newNodes.interact = allStyle

            Array.from(currentDom.attributes).forEach(item => {
              const initarrs = ['data-container-id', 'class', 'data-component', 'data-style', 'data-media', 'data-event']
              !initarrs.includes(item.name) && (newNodes.attrs[item.name] = item.value)
            })
            if (code === 'custom' && !currentDom?.innerHTML.includes('data-component')) {
              newNodes.data.text = currentDom?.innerHTML
            }

            // 匹配容器子长度
            const containerNumKey = newNodes.code.split('-')[1] ? newNodes.code.split('-')[0] + 'Num' : null
            if (containerNumKey) {
              let childNodesLength = 0
              let containerNum = {}
             currentDom?.childNodes?.length > 0 && currentDom.childNodes.forEach(item => {
                if (item.nodeType !== Node.TEXT_NODE && item.nodeType !== Node.COMMENT_NODE) {
                  ++childNodesLength
                }
              })
             containerNum[containerNumKey] = childNodesLength
             newNodes.childNodesLength = childNodesLength
             newNodes.data = containerNum
             newNodes.defaultData = {
               layoutNum: [1, 2, 3, 4],
               layoutDirection: 'row'
             }
             containerNum = null
            }

            if (newNodes.code === 'author-box') {
              Object.assign(newNodes.data, {
                currAuthor: {},
                name: '',
                url: '',
                width: '100px',
                height: '100px'
              })
            }

            // 非容器组件获取子元素样式
            if (newNodes.code !== 'custom' && !newNodes.code.includes('container')) {
              matchingItemStyle(newNodes, currentDom)
            }

            // 自定义当前节点填充数据
            matchingItemData(newNodes, currentDom)

            // 自定义父节点数据填充
            matchingItemPaPaData(newNodes, currentDom)

            childrenData.push(newNodes)
            newNodes.childrenData = currentDom?.childNodes && ((code === 'custom' && !newNodes.data.text) || code.includes('container')) ? eachHtml(currentDom.childNodes, newNodes) : []
          }

          // newNodes = null
        }
        return childrenData
      }

      // 配色key更新
      let htmlCode = document.querySelector('.code-html').innerHTML
      allRootColorKeys.forEach(key => {
        const colorKey = pageColors[0].color_list.find(item => item.origin_key === key).color_key
        htmlCode = htmlCode.replaceAll(key, colorKey)
      })
      document.querySelector('.code-html').innerHTML = htmlCode
      const dom = document.querySelector('.code-html').childNodes[0]?.classList?.contains('cms-visual-editor-content') ? document.querySelector('.code-html').childNodes[0].childNodes : document.querySelector('.code-html').childNodes
      newPages.pages[0].childrenData.push(...eachHtml(dom))
      const newPagesDataCopy = cloneDeep(newPages.pages[0].childrenData)
      newPages.pages[0].childrenData = []
      for (const item of newPagesDataCopy) {
        const { code, group, name, ...defaultData } = item
        const data = await editor.methods.createComponentData({ code, group, name, ...defaultData })
        newPages.pages[0].childrenData.push(data)
      }
      // 设置页面配色
      newPages.pages[0].style.backgroundColor = 'var(--global-site-painter-color)'
      pageColors[0].color_list.length && (newPages.pages[0].colors = pageColors)

      if (store.state.editor.base.mainType === 2) {
        const content = editor.state.data.pages[0].childrenData.find(item => item.isContent)
        console.log(content, 'contentcontentcontent')
        content.childrenData = newPages.pages[0].childrenData
      } else {
        editor.state.data = Object.assign({}, editor.state.data, newPages)
      }
      console.error(editor.state.data, 'editor.state.dataeditor.state.data')
      console.error(newPages)
    }
onMounted(() => {
      monacoEditor.value = monaco.editor.create(document.getElementById('monaco-ref'), {
        theme: 'vs', // 主题 vs, hc-black, or vs-dark
        value: '', // 默认显示的值
        language: 'json',
        folding: true, // 是否折叠
        foldingHighlight: true, // 折叠等高线
        foldingStrategy: 'indentation', // 折叠方式  auto | indentation
        showFoldingControls: 'always', // 是否一直显示折叠 always | mouseover
        disableLayerHinting: true, // 等宽优化
        emptySelectionClipboard: false, // 空选择剪切板
        selectionClipboard: false, // 选择剪切板
        automaticLayout: true, // 自动布局
        codeLens: true, // 代码镜头
        scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
        colorDecorators: true, // 颜色装饰器
        accessibilitySupport: 'on', // 辅助功能支持  "auto" | "off" | "on"
        lineNumbers: 'on', // 行号 取值: "on" | "off" | "relative" | "interval" | function
        lineNumbersMinChars: 5, // 行号最小字符   number
        enableSplitViewResizing: false,
        wordWrap: 'on',
        formatOnType: true,
        readOnly: false // 是否只读  取值 true | false
      })
      // setValue
      editor.state.base.iscode = true

      // 格式化
      // toRaw(monacoEditor.value).getAction('editor.action.formatDocument').run()
      // toRaw(monacoEditor.value).getAction('editor.action.formatDocument')._run()
      toRaw(monacoEditor.value).trigger('anything', 'editor.action.formatDocument')
      toRaw(monacoEditor.value).setValue(toRaw(monacoEditor.value).getValue())
      monacoEditor.value.onDidChangeModelContent(() => {
        console.log('内容变化 onDidChangeModelContent')
        switch (toRaw(monacoEditor.value).getModel()._languageIdentifier.language) {
          case 'html':
            // if (stringToDom(toRaw(monacoEditor.value).getValue()).parentNode.childNodes.length > 1) {
            //   ElMessage.error('追加代码必须放置cms-visual-editor-content之中')
            //   throw new Error('追加代码必须放置cms-visual-editor-content之中')
            // }
            newPage.html = toRaw(monacoEditor.value).getValue()
            break
          case 'css':
            newPage.css = toRaw(monacoEditor.value).getValue()
            break
          case 'javascript':
            newPage.javascript = toRaw(monacoEditor.value).getValue()
            break
        }
      })
      monacoEditor.value.onDidChangeModelLanguage(() => {
        console.error('语言变化')
      })
      monacoEditor.value.onDidBlurEditorText(() => {
        console.log('失去编辑焦点')
        if (['html', 'css'].includes(toRaw(monacoEditor.value).getModel()._languageIdentifier.language)) {
          // dealCss()
          // domToJson()
        }
      })
    })
    const confirmModel = () => {
      isImportData.value = true
      nextTick(() => {
        dealCss()
        dealJs()
        // const a = 1
        // if (a) return
        domToJson()

        setTimeout(() => {
          // getReturnContent()
          editor.state.base.iscode = true
          isImportData.value = false
        }, 1000)
      })
    }
    return () => (
      <div class="code-box">
        {isImportData.value && <div class="code-preview">
          <style>{newPage.defaultCss}</style>
          <style title='code-preview-css'>{newPage.css}</style>
          <div vHtml={newPage.html} class="code-html"></div>
          <script src="https://code.jquery.com/jquery-3.6.2.js" integrity="sha256-pkn2CUZmheSeyssYw3vMp1+xyub4m+e+QK4sQskvuo4=" crossorigin="anonymous"></script>
          {/* <script>{newPage.javascript}</script> */}
        </div>}
        <div id="monaco-ref"></div>
        <div class="code-btn-group">
          <el-button-group class="ml-4">
            <el-button onClick={ () => changeModel('html') }>
              HTML
            </el-button>
            <el-button onClick={ () => changeModel('css') }>
              CSS
            </el-button>
            <el-button onClick={ () => changeModel('javascript') }>
              JS
            </el-button>
            {/* 测试,html 和css 要同时修改转JSON才会有效 */}
            <el-button onClick={ () => changeModel('json') }>
              JSON
            </el-button>
            <el-button onClick={ confirmModel }>
              确认导入
            </el-button>

          </el-button-group>

        </div>
      </div>
    )
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值