Explorar el Código

feat: 配置tab入口代码

王家程 hace 3 años
padre
commit
f840f28600

+ 0 - 27
.github/workflows/ci.yml

@@ -1,27 +0,0 @@
-name: CI
-
-on:
-  push:
-    branches:
-      - master
-
-jobs:
-  build-and-deploy:
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - name: Checkout
-      uses: actions/checkout@v1
-    
-    - name: 打包构建
-      run: |
-        npm install
-        npm run build
-
-    - name: 发布
-      uses: JamesIves/github-pages-deploy-action@releases/v3
-      with:
-        ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
-        BRANCH: gh-pages
-        FOLDER: dist

+ 1 - 1
package.json

@@ -23,7 +23,7 @@
     "vue-router": "^3.5.1",
     "vuedraggable": "^2.23.2",
     "vuex": "^3.6.2",
-    "wisdom-ui": "1.0.1-rc.1"
+    "wisdom-ui": "^1.0.1"
   },
   "devDependencies": {
     "@vue/cli-plugin-babel": "~4.4.0",

+ 5 - 1
public/preview.html

@@ -5,7 +5,11 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
-    <title>form-generator-preview</title>
+    <title>huikaifa-preview</title>
+    <link href="https://lib.baomitu.com/element-ui/2.13.2/theme-chalk/index.css" rel="stylesheet">
+    <script src="https://lib.baomitu.com/vue/2.6.11/vue.min.js"></script>
+    <script src="https://lib.baomitu.com/vue-router/3.1.3/vue-router.min.js"></script>
+    <script src="https://lib.baomitu.com/element-ui/2.13.2/index.js"></script>
     <style>
       body{
         margin: 0;

+ 1 - 1
src/components/generator/js.js

@@ -266,7 +266,7 @@ function buildOptionMethod(methodName, model, methodList, scheme) {
 // wc js整体拼接
 function buildWcExport(config, type, props, methods) {
   const str = `
-  import tab1 from './tab1/index'
+  import tab1 from './tab/index'
 
   ${exportDefault} {
   name: 'ContentComp',

+ 2 - 2
src/components/generator/table.js

@@ -2,14 +2,14 @@
  * @Author: WangJiaCheng
  * @Date: 2021-07-26 14:27:27
  * @LastEditors: WangJiaCheng
- * @LastEditTime: 2021-07-27 15:37:03
+ * @LastEditTime: 2021-07-29 11:06:30
  * @Description: table生成
  */
 import { exportDefault, titleCase, deepClone } from '@/utils/index'
 
 export function buildTable(formData) {
   const { list = [] } = formData
-  const child = list.map(item => `<el-table-column prop="${item.field}" label="${item.name}"></el-table-column>`)
+  const child = list.map(item => `<el-table-column prop="${item.field}" label="${item.name}"></el-table-column>`).join('\n')
   const str = `<w-table
     v-loading="tableLoading"
     :data="tableData"

+ 93 - 0
src/components/generator/tabs.js

@@ -0,0 +1,93 @@
+/*
+ * @Author: WangJiaCheng
+ * @Date: 2021-07-29 11:19:04
+ * @LastEditors: WangJiaCheng
+ * @LastEditTime: 2021-07-29 16:19:09
+ * @Description: tabs 配置
+ */
+import { exportDefault } from '@/utils/index'
+
+export function buildTabs(formData) {
+  const { list = [] } = formData
+  const str = `
+  <div class="tab">
+    ${buildHeader(formData)}
+    <search-form
+      @search="handleSearch"
+      @reset="handleReset"
+    />
+    <data-table
+      ref="dataTable"
+      :loading="tableLoading"
+      :approveData="approveData"
+      @page="handlePage"
+    />
+  </div>
+  `
+  return str
+}
+
+function buildHeader(data) {
+  const btn = []
+  if (data.btn && data.btn.length > 0) {
+    data.btn.forEach((item, index) => {
+      btn.push(`<el-button size="small" ${index === 0 ? 'type="primary"' : ''} @click="handleClick${index}">
+        ${item}
+      </el-button>`)
+    })
+  }
+  const str = `<w-header
+    desc="${data.headerDesc}"
+  >
+    ${btn.length > 0 ? `<template v-slot:right>${btn.join('')}</template>` : ''}
+  </w-header>`
+  return str
+}
+
+export function makeTabsJs(formData) {
+  const str = `
+  import SearchForm from './components/search_from'
+  import DataTable from './components/data_table'
+
+  ${exportDefault} {
+    name: 'Tab',
+    components: {
+      SearchForm,
+      DataTable,
+    },
+    props: {
+      activeTab: String
+    },
+    data () {
+      return {
+        tableLoading: false,
+        page: {
+          pageSize: 20,
+          pageNum: 1
+        }
+      }
+    },
+    mounted () {
+    },
+    methods: {
+      getList (data = {}) {
+        this.tableLoading = true
+      },
+      handlePage (data) {
+        this.page = { ...this.page, ...data }
+        this.getList()
+      },
+      handleSearch (query) {
+        this.tableLoading = true
+        this.page.pageNum = 1
+        this.getList(query)
+      },
+      handleReset () {
+        this.tableLoading = true
+        this.page.pageNum = 1
+        this.getList()
+      }
+    }
+  }`
+  return str
+}

+ 6 - 6
src/views/Page/Edit/PageConfig.vue

@@ -2,7 +2,7 @@
  * @Author: WangJiaCheng
  * @Date: 2021-05-11 11:18:32
  * @LastEditors: WangJiaCheng
- * @LastEditTime: 2021-07-26 10:47:11
+ * @LastEditTime: 2021-07-29 16:04:02
  * @Description: 页面配置
 -->
 <template>
@@ -21,7 +21,7 @@
       </el-form-item>
       <el-form-item label="Header 按钮">
         <el-tag
-          v-for="tag in dynamicTags"
+          v-for="tag in btnTags"
           :key="tag"
           closable
           :disable-transitions="false"
@@ -69,7 +69,7 @@ export default {
           { required: true, message: '请输入Header描述' }
         ]
       },
-      dynamicTags: [],
+      btnTags: [],
       inputVisible: false,
       inputValue: ''
     }
@@ -77,7 +77,7 @@ export default {
   methods: {
     ...mapMutations('page', ['setPageConfig', 'setActiveTab']),
     handleClose(tag) {
-      this.dynamicTags.splice(this.dynamicTags.indexOf(tag), 1)
+      this.btnTags.splice(this.btnTags.indexOf(tag), 1)
     },
     showInput() {
       this.inputVisible = true
@@ -88,7 +88,7 @@ export default {
     handleInputConfirm() {
       const { inputValue } = this
       if (inputValue) {
-        this.dynamicTags.push(inputValue)
+        this.btnTags.push(inputValue)
       }
       this.inputVisible = false
       this.inputValue = ''
@@ -96,7 +96,7 @@ export default {
     next() {
       this.$refs.pageForm.validate(valid => {
         if (valid) {
-          this.setPageConfig(this.formData)
+          this.setPageConfig({ ...this.formData, btn: this.btnTags })
           this.setActiveTab('form')
         }
       })

+ 95 - 26
src/views/Page/Edit/Preview.vue

@@ -2,52 +2,121 @@
  * @Author: WangJiaCheng
  * @Date: 2021-05-12 15:45:40
  * @LastEditors: WangJiaCheng
- * @LastEditTime: 2021-07-16 10:01:07
+ * @LastEditTime: 2021-07-29 17:06:32
  * @Description:
 -->
 <template>
   <div class="preview">
-    预览
-    <div>
-      <el-button type="primary">保存页面</el-button>
-      <el-button @click="renderCode">生成代码</el-button>
+    <div class="left">
+      <el-tree
+        default-expand-all
+        highlight-current
+        :data="data"
+        @node-click="handleNodeClick"
+      />
+    </div>
+    <div class="right">
+      <code-editor
+        :form-data="formData"
+        :code-type="codeType"
+      />
     </div>
   </div>
 </template>
 
 <script>
-import {
-  makeUpHtml, vueTemplate, vueScript, cssStyle
-} from '@/components/generator/html'
-import { makeUpJs } from '@/components/generator/js'
-import { makeUpCss } from '@/components/generator/css'
-import loadBeautifier from '@/utils/loadBeautifier'
-import {
-  beautifierConf
-} from '@/utils/index'
+import { mapMutations, mapState } from 'vuex'
 
-let beautifier
+import CodeEditor from '@/views/components/CodeEditor'
 
 export default {
   name: 'Preview',
-  mounted() {
-    loadBeautifier(btf => {
-      beautifier = btf
+  components: {
+    CodeEditor
+  },
+  data() {
+    return {
+      formData: {},
+      codeType: 'page',
+      data: [
+        {
+          label: 'page',
+          children: [
+            {
+              label: 'index.vue',
+              value: 'page'
+            },
+            {
+              label: 'tab',
+              children: [
+                {
+                  label: 'index.vue',
+                  value: 'tab'
+                },
+                {
+                  label: 'components',
+                  children: [
+                    {
+                      label: 'data_table.vue',
+                      value: 'list'
+                    },
+                    {
+                      label: 'search_form.vue',
+                      value: 'form'
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  },
+  computed: {
+    ...mapState({
+      pageConfig: state => state.page.pageConfig,
+      formConfig: state => state.page.formConfig,
+      listConfig: state => state.page.listConfig
     })
   },
+  mounted() {
+  },
   methods: {
-    renderCode() {
-      this.AssembleFormData()
-      const script = vueScript(makeUpJs(this.formData, 'file'))
-      const html = vueTemplate(makeUpHtml(this.formData, 'file'))
-      const css = cssStyle(makeUpCss(this.formData))
-      console.log('code', this.formData)
-      return beautifier.html(html + script + css, beautifierConf.html)
+    handleNodeClick(data) {
+      if (data.value) {
+        this.codeType = data.value
+        switch (data.value) {
+          case 'page':
+            this.formData = this.pageConfig
+            break
+          case 'tab':
+            this.formData = this.pageConfig
+            break
+          case 'form':
+            this.formData = this.formConfig
+            break
+          case 'list':
+            this.formData = this.listConfig
+            break
+          default:
+            break
+        }
+      }
     }
   }
 }
 </script>
 
 <style lang="less">
-
+  .preview {
+    display: flex;
+    justify-content: space-between;
+    .left {
+      width: 200px;
+    }
+    .right {
+      width: 80%;
+    }
+  }
 </style>

+ 231 - 0
src/views/components/CodeEditor.vue

@@ -0,0 +1,231 @@
+<!--
+ * @Author: WangJiaCheng
+ * @Date: 2021-07-28 14:18:16
+ * @LastEditors: WangJiaCheng
+ * @LastEditTime: 2021-07-29 16:37:04
+ * @Description: 代码编辑
+-->
+<template>
+  <div style="height:100%">
+    <el-row style="height:100%;overflow:auto">
+      <el-col :md="24" :lg="12" class="left-editor">
+        <el-tabs v-model="activeTab" type="card" class="editor-tabs">
+          <el-tab-pane name="html">
+            <span slot="label">
+              <i v-if="activeTab==='html'" class="el-icon-edit" />
+              <i v-else class="el-icon-document" />
+              template
+            </span>
+          </el-tab-pane>
+          <el-tab-pane name="js">
+            <span slot="label">
+              <i v-if="activeTab==='js'" class="el-icon-edit" />
+              <i v-else class="el-icon-document" />
+              script
+            </span>
+          </el-tab-pane>
+          <el-tab-pane name="css">
+            <span slot="label">
+              <i v-if="activeTab==='css'" class="el-icon-edit" />
+              <i v-else class="el-icon-document" />
+              css
+            </span>
+          </el-tab-pane>
+        </el-tabs>
+        <div v-show="activeTab==='html'" id="codeEditorHtml" class="tab-editor" />
+        <div v-show="activeTab==='js'" id="codeEditorJs" class="tab-editor" />
+        <div v-show="activeTab==='css'" id="codeEditorCss" class="tab-editor" />
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { parse } from '@babel/parser'
+import ClipboardJS from 'clipboard'
+import { saveAs } from 'file-saver'
+import {
+  makeUpHtml, vueTemplate, vueScript, cssStyle, makeUpWcHtml
+} from '@/components/generator/html'
+import { makeUpJs, makeUpWcJs } from '@/components/generator/js'
+import { buildTable, makeTableJs } from '@/components/generator/table'
+import { buildTabs, makeTabsJs } from '@/components/generator/tabs'
+import { makeUpCss } from '@/components/generator/css'
+import { exportDefault, beautifierConf } from '@/utils/index'
+import loadMonaco from '@/utils/loadMonaco'
+import loadBeautifier from '@/utils/loadBeautifier'
+
+const editorObj = {
+  html: null,
+  js: null,
+  css: null
+}
+const mode = {
+  html: 'html',
+  js: 'javascript',
+  css: 'css'
+}
+let beautifier
+let monaco
+
+export default {
+  name: 'CodeEditor',
+  props: {
+    codeType: {
+      type: String,
+      default: 'page'
+    },
+    formData: {
+      type: Object,
+      default() {
+        return {}
+      }
+    },
+    generateConf: {
+      type: Object,
+      default() {
+        return {}
+      }
+    }
+  },
+  data() {
+    return {
+      activeTab: 'html',
+      htmlCode: '',
+      jsCode: '',
+      cssCode: '',
+      codeFrame: '',
+      isIframeLoaded: false,
+      isInitcode: false, // 保证open后两个异步只执行一次runcode
+      isRefreshCode: false, // 每次打开都需要重新刷新代码
+      scripts: [],
+      links: [],
+      monaco: null
+    }
+  },
+  watch: {
+    codeType: {
+      handler(val) {
+        if (val) {
+          this.renderCode()
+        }
+      }
+    }
+  },
+  mounted() {
+    window.addEventListener('keydown', this.preventDefaultSave)
+    const clipboard = new ClipboardJS('.copy-btn', {
+      text: trigger => {
+        const codeStr = this.generateCode()
+        this.$notify({
+          title: '成功',
+          message: '代码已复制到剪切板,可粘贴。',
+          type: 'success'
+        })
+        return codeStr
+      }
+    })
+    clipboard.on('error', e => {
+      this.$message.error('代码复制失败')
+    })
+  },
+  beforeDestroy() {
+    window.removeEventListener('keydown', this.preventDefaultSave)
+  },
+  methods: {
+    setEditorValue(id, type, codeStr) {
+      if (editorObj[type]) {
+        editorObj[type].setValue(codeStr)
+      } else {
+        editorObj[type] = monaco.editor.create(document.getElementById(id), {
+          value: codeStr,
+          theme: 'vs-dark',
+          language: mode[type],
+          automaticLayout: true
+        })
+      }
+      // ctrl + s 刷新
+      editorObj[type].onKeyDown(e => {
+        if (e.keyCode === 49 && (e.metaKey || e.ctrlKey)) {
+          this.runCode()
+        }
+      })
+    },
+    renderCode() {
+      const { type = 'file' } = this.generateConf
+      if (this.codeType === 'page') {
+        this.htmlCode = makeUpWcHtml(this.formData, type)
+        this.jsCode = makeUpWcJs(this.formData, type)
+        this.cssCode = makeUpCss(this.formData)
+      }
+      if (this.codeType === 'tab') {
+        this.htmlCode = buildTabs(this.formData, type)
+        this.jsCode = makeTabsJs(this.formData, type)
+        this.cssCode = makeUpCss(this.formData)
+      }
+      if (this.codeType === 'list') {
+        this.htmlCode = buildTable(this.formData, type)
+        this.jsCode = makeTableJs(this.formData, type)
+        this.cssCode = makeUpCss(this.formData)
+      }
+      if (this.codeType === 'form') {
+        this.htmlCode = makeUpHtml(this.formData.formModel, type)
+        this.jsCode = makeUpJs(this.formData.formModel, type)
+        this.cssCode = makeUpCss(this.formData.formModel)
+      }
+
+      loadBeautifier(btf => {
+        beautifier = btf
+        this.htmlCode = beautifier.html(this.htmlCode, beautifierConf.html)
+        this.jsCode = beautifier.js(this.jsCode, beautifierConf.js)
+        // this.cssCode = beautifier.css(this.cssCode, beautifierConf.html)
+
+        loadMonaco(val => {
+          monaco = val
+          this.setEditorValue('codeEditorHtml', 'html', this.htmlCode)
+          this.setEditorValue('codeEditorJs', 'js', this.jsCode)
+          this.setEditorValue('codeEditorCss', 'css', this.cssCode)
+        })
+      })
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+@import '@/styles/mixin.scss';
+.tab-editor {
+  position: absolute;
+  top: 33px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  font-size: 14px;
+}
+.left-editor {
+  position: relative;
+  height: 500px;
+  background: #1e1e1e;
+  overflow: hidden;
+  width: 100%;
+}
+.setting{
+  position: absolute;
+  right: 15px;
+  top: 3px;
+  color: #a9f122;
+  font-size: 18px;
+  cursor: pointer;
+  z-index: 1;
+}
+.right-preview {
+  height: 100%;
+  .result-wrapper {
+    height: calc(100vh - 33px);
+    width: 100%;
+    overflow: auto;
+    padding: 12px;
+    box-sizing: border-box;
+  }
+}
+</style>

+ 4 - 4
src/views/components/previewCode.vue

@@ -50,15 +50,15 @@
                 关闭
               </span>
             </div>
-            <!-- <iframe
+            <iframe
               v-show="isIframeLoaded"
               ref="previewPage"
               class="result-wrapper"
               frameborder="0"
               src="preview.html"
               @load="iframeLoad"
-            /> -->
-            <!-- <div v-show="!isIframeLoaded" v-loading="true" class="result-wrapper" /> -->
+            />
+            <div v-show="!isIframeLoaded" v-loading="true" class="result-wrapper" />
           </el-col>
         </el-row>
       </div>
@@ -250,7 +250,7 @@ export default {
               links: this.links
             }
           }
-
+          console.log('run', postData)
           this.$refs.previewPage.contentWindow.postMessage(
             postData,
             location.origin

+ 1 - 1
src/views/Form/preview/main.js → src/views/preview/main.js

@@ -33,7 +33,7 @@ function init(event) {
     }
 
     $previewApp.innerHTML = `${links}<style>${code.css}</style><div id="app"></div>`
-
+    console.log('run2', code)
     if (Array.isArray(code.scripts) && code.scripts.length > 0) {
       loadScriptQueue(code.scripts, () => {
         newVue(attrs, code.js, code.html)

+ 16 - 16
vue.config.js

@@ -17,22 +17,22 @@ function resolve(dir) {
 
 module.exports = {
   publicPath: '/',
-  // pages: {
-  //   index: {
-  //     entry: 'src/views/index/main.js',
-  //     template: 'public/index.html',
-  //     filename: 'index.html',
-  //     chunks: ['chunk-vendors', 'chunk-common', 'index'],
-  //     minify
-  //   },
-  //   preview: {
-  //     entry: 'src/views/preview/main.js',
-  //     template: 'public/preview.html',
-  //     filename: 'preview.html',
-  //     chunks: ['chunk-vendors', 'chunk-common', 'preview'],
-  //     minify
-  //   }
-  // },
+  pages: {
+    index: {
+      entry: 'src/main.js',
+      template: 'public/index.html',
+      filename: 'index.html',
+      chunks: ['chunk-vendors', 'chunk-common', 'index'],
+      minify
+    },
+    preview: {
+      entry: 'src/views/preview/main.js',
+      template: 'public/preview.html',
+      filename: 'preview.html',
+      chunks: ['chunk-vendors', 'chunk-common', 'preview'],
+      minify
+    }
+  },
   devServer: {
     overlay: false
   },

+ 3 - 3
yarn.lock

@@ -7746,9 +7746,9 @@ which@^2.0.1:
   dependencies:
     isexe "^2.0.0"
 
-wisdom-ui@1.0.1-rc.1:
-  version "1.0.1-rc.1"
-  resolved "http://nexus.wisdomcity.com.cn/repository/wisdomcity-npm-group/wisdom-ui/-/wisdom-ui-1.0.1-rc.1.tgz#69fbca27b31907cd440f50b39e541a043eea6fff"
+wisdom-ui@^1.0.1:
+  version "1.0.1"
+  resolved "http://nexus.wisdomcity.com.cn/repository/wisdomcity-npm-group/wisdom-ui/-/wisdom-ui-1.0.1.tgz#fe379e11959b19d09f18d40800457176547555fe"
   dependencies:
     "@riophae/vue-treeselect" "^0.4.0"
     core-js "^3.6.4"