소스 검색

coding: 模版页面提交

zhangqin 1 개월 전
부모
커밋
1c0eb942b2

+ 2 - 2
src/api/adv/landingPageTemplate.js

@@ -57,8 +57,8 @@ export function batchDelTemplate(ids) {
 // 启用/禁用模板
 export function updateTemplateStatus(id, status) {
   return request({
-    url: '/landing-page-templates/' + id + '/status',
-    method: 'put',
+    url: '/landing-page-templates/enable/' +id ,
+    method: 'post',
     params: { status }
   })
 }

BIN
src/assets/images/image_de.png


BIN
src/assets/images/link-button.gif


BIN
src/assets/images/qr_code.png


+ 36 - 4
src/components/H5/FormWrapper.vue

@@ -26,6 +26,34 @@
       @update:config="updateFormConfig"
     />
 
+    <!-- Form component form items -->
+    <form-form-items
+      v-if="form.type === 'h5-form'"
+      :config="form"
+      @update:config="updateFormConfig"
+    />
+
+    <!-- Link Button component form items -->
+    <link-button-form-items
+      v-if="form.type === 'h5-link-button'"
+      :config="form"
+      @update:config="updateFormConfig"
+    />
+
+    <!-- Add Wechat Button component form items -->
+    <add-wechat-button-form-items
+      v-if="form.type === 'h5-add-wechat-button'"
+      :config="form"
+      @update:config="updateFormConfig"
+    />
+
+    <!-- Qrcode component form items -->
+    <qrcode-form-items
+      v-if="form.type === 'h5-qrcode'"
+      :config="form"
+      @update:config="updateFormConfig"
+    />
+
     <chat-form-items
       v-if="form.type === 'h5-chat'"
       :config="form"
@@ -38,7 +66,6 @@
       :index="index"
       :list="list"
       @bottom-change="handleBottomChange"
-      @delete="handleDelete"
       @update:config="updateFormConfig"
     />
   </el-form>
@@ -50,6 +77,10 @@ import ImageFormItems from './config-item/h5-image-config.vue';
 import SepFormItems from './config-item/h5-sep-config.vue';
 import CommonFormItems from './config-item/common-config.vue';
 import CountdownFormItems from './config-item/h5-countdown-config.vue';
+import FormFormItems from './config-item/h5-form-config.vue';
+import LinkButtonFormItems from './config-item/h5-link-button-config.vue';
+import AddWechatButtonFormItems from './config-item/h5-add-wechat-button-config.vue';
+import QrcodeFormItems from './config-item/h5-qrcode-config.vue';
 import ChatFormItems from './config-item/h5-chat-config.vue'
 export default {
   name: 'FormWrapper',
@@ -59,6 +90,10 @@ export default {
     SepFormItems,
     CommonFormItems,
     CountdownFormItems,
+    FormFormItems,
+    LinkButtonFormItems,
+    AddWechatButtonFormItems,
+    QrcodeFormItems,
     ChatFormItems
   },
   props: {
@@ -108,9 +143,6 @@ export default {
     },
     removeClassFromItem(className) {
       this.form.classText = this.form.classText.filter(cls => cls !== className);
-    },
-    handleDelete() {
-      this.$emit('delete', this.index);
     }
   }
 }

+ 1 - 12
src/components/H5/config-item/common-config.vue

@@ -1,12 +1,7 @@
 // CommonFormItems.vue - Common form items shared across all components
 <template>
   <div id="h5-config-common" ref="commonForm">
-    <el-form-item label="是否底部">
-      <el-switch v-model="config.fixe" @change="handleBottom"></el-switch>
-    </el-form-item>
-    <el-form-item label="点击是否加微">
-      <el-switch v-model="config.addWxFun"/>
-    </el-form-item>
+
     <el-form-item label="获客链接" v-if="config.addWxFun">
       <el-select v-model="config.workUrl" filterable @change="val => updateConfig('workUrl', val)" placeholder="请选择">
         <el-option
@@ -18,9 +13,6 @@
       </el-select>
       <el-button type="primary" @click="copyLink">复制链接</el-button>
     </el-form-item>
-    <el-form-item>
-      <el-button type="danger" @click="handleDelete">删 除</el-button>
-    </el-form-item>
   </div>
 </template>
 
@@ -78,9 +70,6 @@ export default {
     },
     handleBottom() {
       this.$emit('bottom-change');
-    },
-    handleDelete() {
-      this.$emit('delete');
     }
   }
 }

+ 150 - 0
src/components/H5/config-item/h5-add-wechat-button-config.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="add-wechat-button-config">
+    <div class="config-block">
+      <h3>加微按钮配置</h3>
+
+      <!-- 按钮样式选择 -->
+      <el-form-item label="按钮样式">
+        <el-radio-group v-model="localConfig.buttonStyle" @change="handleStyleChange">
+          <el-radio label="text">文字</el-radio>
+          <el-radio label="image">图片</el-radio>
+        </el-radio-group>
+      </el-form-item>
+
+      <!-- 文字样式配置 -->
+      <template v-if="localConfig.buttonStyle === 'text'">
+        <el-form-item label="按钮文字">
+          <el-input 
+            v-model="localConfig.buttonText" 
+            placeholder="请输入按钮文字"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="按钮背景色">
+          <el-color-picker
+            v-model="localConfig.buttonColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="按钮文字色">
+          <el-color-picker
+            v-model="localConfig.buttonTextColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+
+      <!-- 图片样式配置 -->
+      <template v-if="localConfig.buttonStyle === 'image'">
+        <el-form-item label="按钮图片">
+          <image-upload 
+            v-model="localConfig.buttonImage" 
+            :file-type='["png", "jpg", "jpeg", "gif"]' 
+            :limit="1"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+
+      <!-- 其他设置 -->
+      <el-divider></el-divider>
+      <h4>其他设置</h4>
+      <el-form-item label="获客助手跳转">
+        <el-switch v-model="localConfig.enableGetCustomerAssistant" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item label="跟随屏幕">
+        <el-switch v-model="localConfig.followScreen" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item label="默认尝试拉起微信">
+        <el-switch v-model="localConfig.enableDefaultWechat" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item label="启用小程序授权">
+        <el-switch v-model="localConfig.enableMiniProgram" @change="handleConfigChange" />
+      </el-form-item>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'AddWechatButtonConfigComponent',
+
+  props: {
+    config: {
+      type: Object,
+      required: false,
+      default: () => ({
+        buttonStyle: 'text',
+        buttonText: '提交',
+        buttonColor: '#FF5A5A',
+        buttonTextColor: '#ffffff',
+        buttonImage: '',
+        enableGetCustomerAssistant: true,
+        followScreen: true,
+        enableDefaultWechat: false,
+        enableMiniProgram: false
+      })
+    }
+  },
+
+  data() {
+    return {
+      localConfig: JSON.parse(JSON.stringify(this.config))
+    }
+  },
+
+  methods: {
+    handleStyleChange() {
+      // 切换样式时不清空图片,保持默认值
+      this.handleConfigChange()
+    },
+    handleConfigChange() {
+      this.$emit('update:config', JSON.parse(JSON.stringify(this.localConfig)))
+    }
+  },
+
+  watch: {
+    config: {
+      handler(newVal) {
+        this.localConfig = JSON.parse(JSON.stringify(newVal))
+      },
+      deep: true
+    },
+    'localConfig.buttonImage'(newVal) {
+      // 监听图片变化,实时更新父组件
+      this.handleConfigChange()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.add-wechat-button-config {
+  width: 100%;
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
+}
+
+.config-block {
+  padding: 0px 15px;
+}
+
+h3 {
+  font-weight: 500;
+  margin: 15px 0 10px 0;
+  color: #303133;
+  font-size: 16px;
+  border-bottom: 1px solid #EBEEF5;
+  padding-bottom: 10px;
+}
+
+.el-form-item {
+  margin-bottom: 16px;
+}
+</style>

+ 137 - 0
src/components/H5/config-item/h5-form-config.vue

@@ -0,0 +1,137 @@
+<template>
+  <div class="form-config">
+    <div class="config-block">
+      <h3>表单配置</h3>
+
+      <!-- 第一行图片配置 -->
+      <el-form-item label="图片显示">
+        <el-switch v-model="localConfig.showImage" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item v-if="localConfig.showImage" label="上传图片">
+        <image-upload 
+          v-model="localConfig.formImage" 
+          :file-type='["png", "jpg", "jpeg", "gif"]' 
+          :limit="1"
+          @change="handleConfigChange"
+        />
+      </el-form-item>
+
+      <!-- 提交按钮配置 -->
+      <el-form-item label="提交按钮显示">
+        <el-switch v-model="localConfig.showSubmitBtn" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item v-if="localConfig.showSubmitBtn" label="按钮样式">
+        <el-radio-group v-model="localConfig.submitBtnStyle" @change="handleConfigChange">
+          <el-radio label="text">文字</el-radio>
+          <el-radio label="image">图片</el-radio>
+        </el-radio-group>
+      </el-form-item>
+
+      <!-- 文字+背景色样式配置 -->
+      <template v-if="localConfig.showSubmitBtn && localConfig.submitBtnStyle === 'text'">
+        <el-form-item label="按钮文字">
+          <el-input 
+            v-model="localConfig.submitBtnText" 
+            placeholder="请输入按钮文字"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="按钮颜色">
+          <el-color-picker
+            v-model="localConfig.submitBtnColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+
+      <!-- 图片样式配置 -->
+      <el-form-item v-if="localConfig.showSubmitBtn && localConfig.submitBtnStyle === 'image'" label="按钮图片">
+        <image-upload 
+          v-model="localConfig.submitBtnImage" 
+          :file-type='["png", "jpg", "jpeg", "gif"]' 
+          :limit="1"
+          @change="handleConfigChange"
+        />
+      </el-form-item>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'FormConfigComponent',
+
+  props: {
+    config: {
+      type: Object,
+      required: false,
+      default: () => ({
+        showImage: true,
+        formImage: '',
+        showSubmitBtn: true,
+        submitBtnStyle: 'text',
+        submitBtnColor: '#409EFF',
+        submitBtnText: '提交',
+        submitBtnImage: ''
+      })
+    }
+  },
+
+  data() {
+    return {
+      localConfig: JSON.parse(JSON.stringify(this.config))
+    }
+  },
+
+  methods: {
+    handleConfigChange() {
+      this.$emit('update:config', JSON.parse(JSON.stringify(this.localConfig)))
+    }
+  },
+
+  watch: {
+    config: {
+      handler(newVal) {
+        this.localConfig = JSON.parse(JSON.stringify(newVal))
+      },
+      deep: true
+    },
+    'localConfig.formImage'(newVal) {
+      // 监听第一行图片变化,实时更新
+      this.handleConfigChange()
+    },
+    'localConfig.submitBtnImage'(newVal) {
+      // 监听提交按钮图片变化,实时更新
+      this.handleConfigChange()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.form-config {
+  width: 100%;
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
+}
+
+.config-block {
+  padding: 0px 15px;
+}
+
+h3 {
+  font-weight: 500;
+  margin: 15px 0 10px 0;
+  color: #303133;
+  font-size: 16px;
+  border-bottom: 1px solid #EBEEF5;
+  padding-bottom: 10px;
+}
+
+.el-form-item {
+  margin-bottom: 16px;
+}
+</style>

+ 29 - 0
src/components/H5/config-item/h5-image-config.vue

@@ -4,6 +4,10 @@
     <el-form-item label="图片">
       <image-upload v-model="config.url" :file-type='["png", "jpg", "jpeg", "gif"]' :limit="1"/>
     </el-form-item>
+    <div class="image-tips">
+      <p>建议尺寸:宽750px,高不限;</p>
+      <p>支持格式:png/jpeg/gif,不大于1M尺寸。</p>
+    </div>
   </div>
 </template>
 
@@ -18,3 +22,28 @@ export default {
   }
 }
 </script>
+
+<style scoped>
+.image-tips {
+  margin-top: 12px;
+  padding: 12px;
+  background-color: #fef0e6;
+  border-left: 3px solid #ff9800;
+  border-radius: 4px;
+}
+
+.image-tips p {
+  margin: 4px 0;
+  color: #ff9800;
+  font-size: 12px;
+  line-height: 1.6;
+}
+
+.image-tips p:first-child {
+  margin-top: 0;
+}
+
+.image-tips p:last-child {
+  margin-bottom: 0;
+}
+</style>

+ 127 - 0
src/components/H5/config-item/h5-link-button-config.vue

@@ -0,0 +1,127 @@
+<template>
+  <div class="link-button-config">
+    <div class="config-block">
+      <h3>跳转按钮配置</h3>
+
+      <!-- 按钮样式选择 -->
+      <el-form-item label="按钮样式">
+        <el-radio-group v-model="localConfig.buttonStyle" @change="handleStyleChange">
+          <el-radio label="text">文字</el-radio>
+          <el-radio label="image">图片</el-radio>
+        </el-radio-group>
+      </el-form-item>
+
+      <!-- 文字样式配置 -->
+      <template v-if="localConfig.buttonStyle === 'text'">
+        <el-form-item label="按钮文字">
+          <el-input 
+            v-model="localConfig.buttonText" 
+            placeholder="请输入按钮文字"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="按钮背景色">
+          <el-color-picker
+            v-model="localConfig.buttonColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="按钮文字色">
+          <el-color-picker
+            v-model="localConfig.buttonTextColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+
+      <!-- 图片样式配置 -->
+      <template v-if="localConfig.buttonStyle === 'image'">
+        <el-form-item label="按钮图片">
+          <image-upload 
+            v-model="localConfig.buttonImage" 
+            :file-type='["png", "jpg", "jpeg", "gif"]' 
+            :limit="1"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'LinkButtonConfigComponent',
+
+  props: {
+    config: {
+      type: Object,
+      required: false,
+      default: () => ({
+        buttonStyle: 'text',
+        buttonText: '提交',
+        buttonColor: '#FF5A5A',
+        buttonTextColor: '#ffffff',
+        buttonImage: ''
+      })
+    }
+  },
+
+  data() {
+    return {
+      localConfig: JSON.parse(JSON.stringify(this.config))
+    }
+  },
+
+  methods: {
+    handleStyleChange() {
+      // 切换样式时不清空图片,保持默认值
+      this.handleConfigChange()
+    },
+    handleConfigChange() {
+      this.$emit('update:config', JSON.parse(JSON.stringify(this.localConfig)))
+    }
+  },
+
+  watch: {
+    config: {
+      handler(newVal) {
+        this.localConfig = JSON.parse(JSON.stringify(newVal))
+      },
+      deep: true
+    },
+    'localConfig.buttonImage'(newVal) {
+      // 监听图片变化,实时更新父组件
+      this.handleConfigChange()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.link-button-config {
+  width: 100%;
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
+}
+
+.config-block {
+  padding: 0px 15px;
+}
+
+h3 {
+  font-weight: 500;
+  margin: 15px 0 10px 0;
+  color: #303133;
+  font-size: 16px;
+  border-bottom: 1px solid #EBEEF5;
+  padding-bottom: 10px;
+}
+
+.el-form-item {
+  margin-bottom: 16px;
+}
+</style>

+ 188 - 0
src/components/H5/config-item/h5-qrcode-config.vue

@@ -0,0 +1,188 @@
+<template>
+  <div class="qrcode-config">
+    <div class="config-block">
+      <h3>二维码配置</h3>
+
+      <!-- 获客助手跳转 -->
+      <el-form-item label="获客助手跳转">
+        <el-switch v-model="localConfig.enableGetCustomerAssistant" @change="handleConfigChange" />
+      </el-form-item>
+
+      <!-- 二维码尺寸 -->
+      <el-form-item label="二维码尺寸(px)">
+        <el-input-number 
+          v-model="localConfig.qrcodeSize" 
+          :min="80"
+          :max="300"
+          @change="handleConfigChange"
+        />
+      </el-form-item>
+
+      <!-- 复制微信号区域 -->
+      <el-divider></el-divider>
+      <h4>复制微信号按钮</h4>
+      <el-form-item label="是否启用">
+        <el-switch v-model="localConfig.showCopyBtn" @change="handleConfigChange" />
+      </el-form-item>
+
+      <template v-if="localConfig.showCopyBtn">
+        <el-form-item label="按钮文字">
+          <el-input 
+            v-model="localConfig.copyBtnText" 
+            placeholder="请输入按钮文字"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="文字颜色">
+          <el-color-picker
+            v-model="localConfig.copyBtnTextColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="填充颜色">
+          <el-color-picker
+            v-model="localConfig.copyBtnColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+      </template>
+
+      <!-- 点击添加老师区域 -->
+      <el-divider></el-divider>
+      <h4>点击添加老师按钮</h4>
+      <el-form-item label="是否启用">
+        <el-switch v-model="localConfig.showClickBtn" @change="handleConfigChange" />
+      </el-form-item>
+
+      <template v-if="localConfig.showClickBtn">
+        <el-form-item label="按钮文字">
+          <el-input 
+            v-model="localConfig.clickBtnText" 
+            placeholder="请输入按钮文字"
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="文字颜色">
+          <el-color-picker
+            v-model="localConfig.clickBtnTextColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="填充颜色">
+          <el-color-picker
+            v-model="localConfig.clickBtnColor"
+            show-alpha
+            @change="handleConfigChange"
+          />
+        </el-form-item>
+
+        <el-form-item label="微信内跳转">
+          <el-switch v-model="localConfig.clickBtnWechatJump" @change="handleConfigChange" />
+        </el-form-item>
+      </template>
+
+      <!-- 其他选项 -->
+      <el-divider></el-divider>
+      <h4>其他设置</h4>
+      <el-form-item label="默认尝试拉起微信">
+        <el-switch v-model="localConfig.enableDefaultWechat" @change="handleConfigChange" />
+      </el-form-item>
+
+      <el-form-item label="启用小程序授权">
+        <el-switch v-model="localConfig.enableMiniProgram" @change="handleConfigChange" />
+      </el-form-item>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'QrcodeConfigComponent',
+
+  props: {
+    config: {
+      type: Object,
+      required: false,
+      default: () => ({
+        qrcodeImage: '',
+        qrcodeSize: 140,
+        showCopyBtn: true,
+        copyBtnText: '复制并添加老师',
+        copyBtnColor: '#FF9500',
+        copyBtnTextColor: '#ffffff',
+        showClickBtn: true,
+        clickBtnText: '点击添加老师',
+        clickBtnColor: '#FF9500',
+        clickBtnTextColor: '#ffffff',
+        clickBtnWechatJump: false,
+        enableGetCustomerAssistant: false,
+        enableDefaultWechat: false,
+        enableMiniProgram: false
+      })
+    }
+  },
+
+  data() {
+    return {
+      localConfig: JSON.parse(JSON.stringify(this.config))
+    }
+  },
+
+  methods: {
+    handleConfigChange() {
+      this.$emit('update:config', JSON.parse(JSON.stringify(this.localConfig)))
+    }
+  },
+
+  watch: {
+    config: {
+      handler(newVal) {
+        this.localConfig = JSON.parse(JSON.stringify(newVal))
+      },
+      deep: true
+    }
+  }
+}
+</script>
+
+<style scoped>
+.qrcode-config {
+  width: 100%;
+  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;
+}
+
+.config-block {
+  padding: 0px 15px;
+}
+
+h3 {
+  font-weight: 500;
+  margin: 15px 0 10px 0;
+  color: #303133;
+  font-size: 16px;
+  border-bottom: 1px solid #EBEEF5;
+  padding-bottom: 10px;
+}
+
+h4 {
+  font-weight: 500;
+  margin: 10px 0 8px 0;
+  color: #606266;
+  font-size: 14px;
+}
+
+.el-form-item {
+  margin-bottom: 16px;
+}
+
+.el-divider {
+  margin: 15px 0;
+}
+</style>

+ 103 - 0
src/components/H5/h5-add-wechat-button.vue

@@ -0,0 +1,103 @@
+<template>
+  <div :class="config.classText.join(' ')">
+    <div class="add-wechat-button-wrapper">
+      <!-- 文字样式按钮 -->
+      <button 
+        v-if="config.buttonStyle === 'text'"
+        class="add-wechat-button text-style"
+        :style="{ 
+          backgroundColor: config.buttonColor,
+          color: config.buttonTextColor
+        }"
+        @click="handleClick"
+      >
+        {{ config.buttonText }}
+      </button>
+
+      <!-- 图片样式按钮 -->
+      <img 
+        v-else-if="config.buttonStyle === 'image'"
+        :src="config.buttonImage || require('@/assets/images/default.jpg')"
+        class="add-wechat-button image-style"
+        @click="handleClick"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'h5-add-wechat-button',
+  props: {
+    config: {
+      type: Object,
+      default: () => {
+        return {
+          classText: ['parent-div'],
+          buttonStyle: 'text',
+          buttonText: '提交',
+          buttonColor: '#FF5A5A',
+          buttonTextColor: '#ffffff',
+          buttonImage: ''
+        }
+      }
+    }
+  },
+  methods: {
+    handleClick() {
+      // 点击事件可在此处理
+    }
+  }
+}
+</script>
+
+<style src="./css/base.css"></style>
+<style lang="scss" scoped>
+.add-wechat-button-wrapper {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+}
+
+.add-wechat-button {
+  border: none;
+  border-radius: 20px;
+  font-size: 16px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  
+  &.text-style {
+    width: calc(100% - 32px);
+    padding: 12px 16px;
+    font-weight: 600;
+    
+    &:hover {
+      opacity: 0.9;
+      transform: translateY(-2px);
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    }
+    
+    &:active {
+      opacity: 0.8;
+      transform: translateY(0);
+    }
+  }
+  
+  &.image-style {
+    max-width: 100%;
+    height: auto;
+    max-height: 80px;
+    object-fit: contain;
+    
+    &:hover {
+      opacity: 0.9;
+      transform: scale(1.02);
+    }
+    
+    &:active {
+      opacity: 0.8;
+      transform: scale(1);
+    }
+  }
+}
+</style>

+ 233 - 0
src/components/H5/h5-form.vue

@@ -0,0 +1,233 @@
+<template>
+  <div :class="config.classText.join(' ')">
+    <div class="form-container">
+      <!-- 第一行:图片 -->
+      <div v-if="config.showImage" class="form-image-row">
+        <img v-if="config.formImage" :src="config.formImage" class="form-image" />
+      </div>
+
+      <!-- 第二行:手机号输入框 -->
+      <div class="form-input-row">
+        <input 
+          type="tel" 
+          class="form-input phone-input" 
+          placeholder="请输入手机号"
+          v-model="phoneNumber"
+        />
+      </div>
+
+      <!-- 第三行:验证码输入框 + 获取验证码按钮 -->
+      <div class="form-code-row">
+        <input 
+          type="text" 
+          class="form-input code-input" 
+          placeholder="请输入验证码"
+          v-model="verifyCode"
+        />
+        <button class="get-code-btn" @click="getVerifyCode">
+          {{ verifyCodeBtnText }}
+        </button>
+      </div>
+
+      <!-- 第四行:提交按钮 -->
+      <div v-if="config.showSubmitBtn" class="form-submit-row">
+        <!-- 文字+背景色样式 -->
+        <button 
+          v-if="config.submitBtnStyle === 'text'"
+          class="submit-btn text-style"
+          :style="{ backgroundColor: config.submitBtnColor }"
+        >
+          {{ config.submitBtnText }}
+        </button>
+        <!-- 图片样式 -->
+        <img 
+          v-else-if="config.submitBtnStyle === 'image'"
+          :src="config.submitBtnImage || require('@/assets/images/link-button.gif')"
+          class="submit-btn image-style"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'h5-form',
+  props: {
+    config: {
+      type: Object,
+      default: () => {
+        return {
+          classText: ['parent-div'],
+          showImage: true,
+          formImage: '',
+          showSubmitBtn: true,
+          submitBtnStyle: 'text', // 'text' 或 'image'
+          submitBtnColor: '#409EFF',
+          submitBtnText: '提交',
+          submitBtnImage: ''
+        }
+      }
+    }
+  },
+  data() {
+    return {
+      phoneNumber: '',
+      verifyCode: '',
+      countdownTime: 0,
+      verifyCodeBtnText: '获取验证码'
+    }
+  },
+  methods: {
+    getVerifyCode() {
+      if (!this.phoneNumber) {
+        this.$message.warning('请先输入手机号');
+        return;
+      }
+      // 启动倒计时
+      this.countdownTime = 60;
+      this.updateButtonText();
+    },
+    updateButtonText() {
+      if (this.countdownTime > 0) {
+        this.verifyCodeBtnText = `${this.countdownTime}s`;
+        this.countdownTime--;
+        setTimeout(() => {
+          this.updateButtonText();
+        }, 1000);
+      } else {
+        this.verifyCodeBtnText = '获取验证码';
+      }
+    }
+  }
+}
+</script>
+
+<style src="./css/base.css"></style>
+<style lang="scss" scoped>
+.form-container {
+  width: 100%;
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.active {
+  border-color: #02ff9b;
+}
+
+.form-image-row {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  margin-bottom: 8px;
+}
+
+.form-image {
+  max-width: 100%;
+  height: auto;
+  max-height: 150px;
+  border-radius: 4px;
+}
+
+.form-input-row,
+.form-code-row {
+  width: 100%;
+  display: flex;
+  gap: 8px;
+}
+
+.form-code-row {
+  justify-content: space-between;
+}
+
+.form-input {
+  border: 1px solid #DCDFE6;
+  border-radius: 20px;
+  padding: 10px 16px;
+  font-size: 14px;
+  color: #303133;
+  
+  &::placeholder {
+    color: #909399;
+  }
+
+  &:focus {
+    outline: none;
+    border-color: #409EFF;
+    box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+  }
+}
+
+.phone-input {
+  width: 100%;
+}
+
+.code-input {
+  flex: 1;
+  min-width: 150px;
+}
+
+.get-code-btn {
+  padding: 10px 16px;
+  border: 1px solid #409EFF;
+  background-color: #fff;
+  color: #409EFF;
+  border-radius: 20px;
+  font-size: 14px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  white-space: nowrap;
+
+  &:hover {
+    background-color: #F0F9FF;
+  }
+
+  &:active {
+    background-color: #E6F2FF;
+  }
+}
+
+.form-submit-row {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  margin-top: 8px;
+}
+
+.submit-btn {
+  border: none;
+  border-radius: 20px;
+  font-size: 16px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  width: calc(100% - 32px);
+
+  &.text-style {
+    padding: 12px 48px;
+    color: #fff;
+    font-weight: 600;
+
+    &:hover {
+      opacity: 0.9;
+    }
+
+    &:active {
+      opacity: 0.8;
+    }
+  }
+
+  &.image-style {
+    max-width: 100%;
+    height: auto;
+    max-height: 60px;
+    object-fit: contain;
+    width: auto;
+
+    &:hover {
+      opacity: 0.9;
+    }
+  }
+}
+</style>

+ 10 - 1
src/components/H5/h5-image.vue

@@ -1,6 +1,6 @@
 <template>
   <div :class="config.classText.join(' ')">
-    <img :src="config.url || require('@/assets/images/default.jpg')" :style="config.style" />
+    <img :src="config.url || require('@/assets/images/image_de.png')" :style="config.style" />
   </div>
 <!--  <img  src="@/assets/images/default.jpg" width="200" height="200" />-->
 </template>
@@ -18,8 +18,17 @@ export default {
 </script>
 <style src="./css/base.css"></style>
 <style lang="scss" scoped>
+div {
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  box-sizing: border-box;
+}
+
 img{
   width: 100% !important;
+  height: auto;
+  display: block;
 }
 .active{
   border-color: #02ff9b;

+ 103 - 0
src/components/H5/h5-link-button.vue

@@ -0,0 +1,103 @@
+<template>
+  <div :class="config.classText.join(' ')">
+    <div class="link-button-wrapper">
+      <!-- 文字样式按钮 -->
+      <button 
+        v-if="config.buttonStyle === 'text'"
+        class="link-button text-style"
+        :style="{ 
+          backgroundColor: config.buttonColor,
+          color: config.buttonTextColor
+        }"
+        @click="handleClick"
+      >
+        {{ config.buttonText }}
+      </button>
+
+      <!-- 图片样式按钮 -->
+      <img 
+        v-else-if="config.buttonStyle === 'image'"
+        :src="config.buttonImage || require('@/assets/images/link-button.gif')"
+        class="link-button image-style"
+        @click="handleClick"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'h5-link-button',
+  props: {
+    config: {
+      type: Object,
+      default: () => {
+        return {
+          classText: ['parent-div'],
+          buttonStyle: 'text',
+          buttonText: '提交',
+          buttonColor: '#FF5A5A',
+          buttonTextColor: '#ffffff',
+          buttonImage: ''
+        }
+      }
+    }
+  },
+  methods: {
+    handleClick() {
+      // 点击事件可在此处理
+    }
+  }
+}
+</script>
+
+<style src="./css/base.css"></style>
+<style lang="scss" scoped>
+.link-button-wrapper {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+}
+
+.link-button {
+  border: none;
+  border-radius: 20px;
+  font-size: 16px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  
+  &.text-style {
+    width: calc(100% - 32px);
+    padding: 12px 16px;
+    font-weight: 600;
+    
+    &:hover {
+      opacity: 0.9;
+      transform: translateY(-2px);
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    }
+    
+    &:active {
+      opacity: 0.8;
+      transform: translateY(0);
+    }
+  }
+  
+  &.image-style {
+    max-width: 100%;
+    height: auto;
+    max-height: 80px;
+    object-fit: contain;
+    
+    &:hover {
+      opacity: 0.9;
+      transform: scale(1.02);
+    }
+    
+    &:active {
+      opacity: 0.8;
+      transform: scale(1);
+    }
+  }
+}
+</style>

+ 129 - 0
src/components/H5/h5-qrcode.vue

@@ -0,0 +1,129 @@
+<template>
+  <div :class="config.classText.join(' ')">
+    <div class="qrcode-container">
+      <div class="qrcode-image-row">
+        <img 
+          :src="config.qrcodeImage || require('@/assets/images/qr_code.png')"
+          class="qrcode-image"
+          :style="{ width: config.qrcodeSize + 'px', height: config.qrcodeSize + 'px' }"
+        />
+      </div>
+      <div class="wechat-number-row">
+        <span class="wechat-number-text">微信号: xxxxxx</span>
+      </div>
+
+      <div v-if="config.showCopyBtn" class="copy-btn-row">
+        <button class="circle-btn copy-btn" :style="{ backgroundColor: config.copyBtnColor, color: config.copyBtnTextColor }">
+          {{ config.copyBtnText }}
+        </button>
+      </div>
+
+      <div v-if="config.showClickBtn" class="click-btn-row">
+        <button class="circle-btn click-btn" :style="{ backgroundColor: config.clickBtnColor, color: config.clickBtnTextColor }">
+          {{ config.clickBtnText }}
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'h5-qrcode',
+  props: {
+    config: {
+      type: Object,
+      default: () => ({
+        classText: ['parent-div'],
+        qrcodeImage: '',
+        qrcodeSize: 140,
+        showCopyBtn: true,
+        copyBtnText: '复制并添加老师',
+        copyBtnColor: '#FF9500',
+        copyBtnTextColor: '#ffffff',
+        showClickBtn: true,
+        clickBtnText: '点击添加老师',
+        clickBtnColor: '#FF9500',
+        clickBtnTextColor: '#ffffff'
+      })
+    }
+  }
+}
+</script>
+
+<style src="./css/base.css"></style>
+<style lang="scss" scoped>
+.qrcode-container {
+  width: 100%;
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  align-items: center;
+}
+
+.qrcode-image-row {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  margin-bottom: 8px;
+}
+
+.qrcode-image {
+  max-width: 100%;
+  height: auto;
+  border-radius: 4px;
+}
+
+.wechat-number-row {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  margin-bottom: 8px;
+}
+
+.wechat-number-text {
+  font-size: 14px;
+  color: #303133;
+  text-align: center;
+}
+
+.copy-btn-row,
+.click-btn-row {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  margin-bottom: 8px;
+}
+
+.circle-btn {
+  border: none;
+  border-radius: 20px;
+  padding: 10px 20px;
+  font-size: 14px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  white-space: nowrap;
+  font-weight: 500;
+
+  &:hover {
+    opacity: 0.9;
+  }
+
+  &:active {
+    opacity: 0.8;
+  }
+}
+
+.copy-btn {
+  min-width: 160px;
+}
+
+.click-btn {
+  min-width: 160px;
+}
+
+.active {
+  border-color: #02ff9b;
+}
+</style>

+ 5 - 2
src/components/H5/h5-sep.vue

@@ -19,8 +19,11 @@ export default {
 </script>
 <style src="./css/base.css"></style>
 <style lang="scss" scoped>
-p{
-  white-space: pre-line;
+div {
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  box-sizing: border-box;
 }
 .active{
   border-color: #02ff9b;

+ 4 - 0
src/components/H5/h5-text.vue

@@ -20,6 +20,10 @@ export default {
 <style lang="scss" scoped>
 p{
   white-space: pre-line;
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  box-sizing: border-box;
 }
 .active{
   border-color: #02ff9b;

+ 696 - 202
src/components/H5Editor/index.vue

@@ -1,40 +1,87 @@
 <template>
   <div class="h5-editor-container">
-    <div class="h5-editor">
-    </div>
-    <el-row :gutter="24">
-      <el-col :span="2" class="button-body">
-        <div v-for="item in componentList">
-          <p/>
-          <div>
-            {{ item.groupName }}
+    <el-row :gutter="16" class="editor-wrapper">
+      <!-- 左侧:组件面板 -->
+      <el-col :span="4" class="left-panel">
+        <div class="panel-header">
+          <h3>组件库</h3>
+        </div>
+        <div class="components-list">
+          <div v-for="(group, idx) in componentList" :key="idx" class="component-group">
+            <div class="group-title">
+              <i class="el-icon-box"></i>
+              {{ group.groupName }}
+            </div>
+            <div class="buttons-wrapper">
+              <div 
+                v-for="comp in group.comps" 
+                :key="comp.type"
+                class="comp-button" 
+                draggable="true"
+                @dragstart="handleDragStart($event, comp)"
+                @dragend="handleDragEnd"
+              >
+                <i :class="comp.icon"></i>
+                <span>{{ comp.label }}</span>
+              </div>
+            </div>
           </div>
-          <el-button class="button-tag" @click="add(item)" v-for="item in item.comps">
-            <!-- 添加图标和文字 -->
-            <i :class="item.icon" style="margin-right: 5px"></i>
-            {{ item.label }}
-          </el-button>
         </div>
       </el-col>
-      <el-col :span="12">
+      
+      <!-- 中间:预览区域 -->
+      <el-col :span="11" class="center-panel">
+        <div class="panel-header">
+          <h3>页面预览</h3>
+          <span class="device-info">手机预览 (375px)</span>
+        </div>
         <div class="parent-container">
-          <div class="view-body">
-            <draggable v-model="list" @end="end">
-              <div v-for="(item, index) in list" :key="index" @click="select(index)" class="view-item">
-                <component :is="item.type" :config="item"/>
+          <div class="view-body" @dragover.prevent="handleDragOver" @drop="handleDrop" @dragleave="handleDragLeave" @mousemove="handleMouseMove">
+            <draggable 
+              v-model="list" 
+              @end="end" 
+              class="draggable-area"
+              animation="200"
+              group="components"
+              :disabled="false"
+              ghost-class="ghost"
+              drag-class="drag"
+            >
+              <div v-for="(item, index) in list" :key="'item-' + index" 
+                   class="draggable-wrapper"
+                   @dragenter="handleItemDragEnter($event, index)"
+                   @dragover="handleItemDragOver($event, index)"
+                   @dragleave="handleItemDragLeave($event, index)">
+                <div @click="select(index)" class="view-item" :class="{active: item.active}">
+                  <component :is="item.type" :config="item"/>
+                  <!-- 删除按钮 -->
+                  <div v-if="item.active" class="delete-button-wrapper" @click.stop="handleDelete(index)">
+                    <i class="el-icon-close"></i>
+                  </div>
+                </div>
               </div>
             </draggable>
+            <div v-if="list.length === 0" class="empty-state">
+              <i class="el-icon-picture"></i>
+              <p>从左侧拖拽或点击添加组件</p>
+            </div>
           </div>
         </div>
       </el-col>
-      <el-col :span="10" style="overflow-y: auto;height:80vh;">
-        <form-wrapper
-          :form="form"
-          :index="index"
-          :list="list"
-          @update:form="updateForm"
-          @delete="del"
-        />
+      
+      <!-- 右侧:属性面板 -->
+      <el-col :span="9" class="right-panel">
+        <div class="panel-header">
+          <h3>属性设置</h3>
+        </div>
+        <div class="properties-wrapper">
+          <form-wrapper
+            :form="form"
+            :index="index"
+            :list="list"
+            @update:form="updateForm"
+          />
+        </div>
       </el-col>
     </el-row>
   </div>
@@ -47,6 +94,10 @@ import H5Image from '@/components/H5/h5-image.vue'
 import H5Button from '@/components/H5/h5-button.vue'
 import H5Sep from '@/components/H5/h5-sep.vue'
 import H5Countdown from '@/components/H5/h5-countdown.vue'
+import H5Form from '@/components/H5/h5-form.vue'
+import H5LinkButton from '@/components/H5/h5-link-button.vue'
+import H5AddWechatButton from '@/components/H5/h5-add-wechat-button.vue'
+import H5Qrcode from '@/components/H5/h5-qrcode.vue'
 import FormWrapper from '@/components/H5/FormWrapper.vue'
 import H5Chat from '@/components/H5/h5-chat.vue'
 import AgentAvatar from '@/assets/images/customer.png?inline'
@@ -59,6 +110,10 @@ export default {
     H5Image,// 对应模板中的<h5-image>
     H5Sep,// 对应模板中的<h5-sep>
     H5Countdown,// 对应模板中的<h5-countdown>
+    H5Form,// 对应模板中的<h5-form>
+    H5LinkButton,// 对应模板中的<h5-link-button>
+    H5AddWechatButton,// 对应模板中的<h5-add-wechat-button>
+    H5Qrcode,// 对应模板中的<h5-qrcode>
     FormWrapper,
     H5Chat
   },
@@ -101,20 +156,44 @@ export default {
             icon: 'el-icon-timer'            // 计时器图标表示倒计时
           },
           {
-            type: 'h5-chat',
-            label: '互动问答',
-            icon: 'el-icon-chat-square'     // 购物袋表示购买相关
+            type: 'h5-form',
+            label: '表单',
+            icon: 'el-icon-document-copy'    // 表单图标
+          },
+          {
+            type: 'h5-link-button',
+            label: '跳转按钮',
+            icon: 'el-icon-connection'       // 跳转图标
+          },
+          {
+            type: 'h5-add-wechat-button',
+            label: '加微按钮',
+            icon: 'el-icon-connection'       // 加微图标
+          },
+          {
+            type: 'h5-qrcode',
+            label: '二维码',
+            icon: 'el-icon-picture'          // 二维码图标
           }
         ]
       }],
       list: [],
-      index: 0,
-      form: {}
+      index: -1,
+      form: {},
+      draggedComponent: null, // 存储被拖拽的组件信息
+      insertIndex: -1 // 存储插入位置
     }
   },
   methods: {
     initData(json) {
-      this.list = JSON.parse(json)
+      try {
+        this.list = JSON.parse(json)
+      } catch (e) {
+        this.list = []
+      }
+      // 重置选中状态和属性表单
+      this.form = {}
+      this.index = -1
     },
     end() {
 
@@ -126,7 +205,6 @@ export default {
         name: item.label,
         fixe: false,
         classText: ['parent-div'],
-        addWxFun: false,
         workUrl: ''
       }
       if (item.type === 'h5-text') {
@@ -142,8 +220,6 @@ export default {
         }
       } else if (item.type === 'h5-image') {
         data.url = null
-      } else if (item.type === 'h5-button') {
-        data.content = '默认文本'
       } else if (item.type === 'h5-sep') {
         data.classText = [...data.classText, 'divider']
         data.style = {
@@ -156,101 +232,46 @@ export default {
         data.minutes = 0
         data.seconds = 0
         data.countdownMode = 1
-      } else if (item.type === 'h5-chat') {
-        data = {...data,
-          ...{
-            messages: [
-              {
-                id: 1,
-                sender: "agent",
-                text: "您好!欢迎报名【中老年健康养生大讲堂】,请仔细答一下问题,便于帮您分配专业的老师进行指导。",
-                welcome: true
-              },
-              {
-                id: 2,
-                sender: "agent",
-                text: "您的年龄?",
-                options: [
-                  { id: 1, text: "45-55岁" },
-                  { id: 2, text: "55-60岁" },
-                  { id: 3, text: "60-65岁" },
-                  { id: 4, text: "65岁以上" }
-                ],
-                userSelection: null
-              },
-              {
-                id: 3,
-                sender: "user",
-                text: '45-55岁',
-                userSelection: { id: 1, text: "45-55岁" }
-              },
-              {
-                id: 4,
-                sender: "agent",
-                text: "更想通过课程学习到哪些知识?",
-                options: [
-                  { id: 1, text: "食疗食补" },
-                  { id: 2, text: "经络疏通" },
-                  { id: 3, text: "脏腑调养" },
-                  { id: 4, text: "启蒙养生" },
-                  { id: 5, text: "以上所有" }
-                ],
-                userSelection: null
-              },
-              {
-                id: 5,
-                sender: "user",
-                text: '食疗食补',
-                userSelection: { id: 1, text: "食疗食补" }
-              }
-            ],
-            agentMsg:[{
-              id: 1,
-              sender: "agent",
-              text: "您好!欢迎报名【中老年健康养生大讲堂】,请仔细答一下问题,便于帮您分配专业的老师进行指导。",
-              welcome: true
-            },
-              {
-                id: 2,
-                sender: "agent",
-                text: "您的年龄?",
-                options: [
-                  { id: 1, text: "45-55岁" },
-                  { id: 2, text: "55-60岁" },
-                  { id: 3, text: "60-65岁" },
-                  { id: 4, text: "65岁以上" }
-                ],
-                userSelection: null
-              },
-              {
-                id: 4,
-                sender: "agent",
-                text: "更想通过课程学习到哪些知识?",
-                options: [
-                  { id: 1, text: "食疗食补" },
-                  { id: 2, text: "经络疏通" },
-                  { id: 3, text: "脏腑调养" },
-                  { id: 4, text: "启蒙养生" },
-                  { id: 5, text: "以上所有" }
-                ],
-                userSelection: null
-              }
-              ],
-            style: {
-              avatar: window.location.origin+AgentAvatar,
-              buttonColor: '#409EFF',
-              textColor: ''
-            }
-          }
-        }
-        console.log(data)
-        let h5chat = this.list.some(item => item.type && item.type.includes('h5-chat'))
-        if (h5chat) {
-          alert('全局只能允许有一个互动问答!')
-          return
-        }
+      } else if (item.type === 'h5-form') {
+        data.showImage = true
+        data.formImage = ''
+        data.showSubmitBtn = true
+        data.submitBtnStyle = 'text'
+        data.submitBtnColor = '#409EFF'
+        data.submitBtnText = '提交'
+        data.submitBtnImage = ''
+      } else if (item.type === 'h5-link-button') {
+        data.buttonStyle = 'text'
+        data.buttonText = '提交'
+        data.buttonColor = '#FF5A5A'
+        data.buttonTextColor = '#ffffff'
+        data.buttonImage = ''
+      } else if (item.type === 'h5-add-wechat-button') {
+        data.buttonStyle = 'text'
+        data.buttonText = '提交'
+        data.buttonColor = '#FF5A5A'
+        data.buttonTextColor = '#ffffff'
+        data.buttonImage = ''
+        data.enableGetCustomerAssistant = true
+        data.followScreen = true
+        data.enableDefaultWechat = false
+        data.enableMiniProgram = false
+      } else if (item.type === 'h5-qrcode') {
+        data.qrcodeImage = ''
+        data.qrcodeSize = 140
+        data.showCopyBtn = true
+        data.copyBtnText = '复制并添加老师'
+        data.copyBtnColor = '#FF9500'
+        data.copyBtnTextColor = '#ffffff'
+        data.showClickBtn = true
+        data.clickBtnText = '点击添加老师'
+        data.clickBtnColor = '#FF9500'
+        data.clickBtnTextColor = '#ffffff'
+        data.clickBtnWechatJump = false
+        data.enableGetCustomerAssistant = false
+        data.enableDefaultWechat = false
+        data.enableMiniProgram = false
       }
-      console.log(this.list)
 
       if (this.index !== null && this.index >= 0 && this.index < this.list.length) {
         this.list.splice(this.index + 1, 0, data)
@@ -260,35 +281,244 @@ export default {
     },
     select(index) {
       this.index = index
-      let className = 'active'
-      this.clearClass(className)
-      this.addClass(className)
-      this.$set(this.list[index], 'active', true)  // 确保响应式更新
-      // 直接绑定list中的对象(关键修改)
+      // 更新所有元素的active状态
+      this.list.forEach((item, i) => {
+        this.$set(item, 'active', i === index)
+      })
+      // 直接绑定list中的对象
       this.form = this.list[index]
     },
-    clearClass(classText) {
-      this.list.forEach(item => {
-        // 推荐使用 $set
-        this.$set(item, 'classText', item.classText.filter(c => c !== classText))
-      })
+
+    del() {
+      if (this.index >= 0 && this.index < this.list.length) {
+        this.list.splice(this.index, 1)
+        this.form = {}
+        this.index = -1
+      }
     },
-    addClass(classText) {
-      this.list[this.index].classText.push(classText)
+    /** 处理删除操作 */
+    handleDelete(index) {
+      this.index = index
+      this.list.splice(index, 1)
+      this.form = {}
+      this.index = -1
     },
+    updateForm(newForm) {
+      // 更新表单数据
+      if (this.index < 0 || this.index >= this.list.length) {
+        return
+      }
 
+      Object.assign(this.form, newForm)
+
+      // 更新列表中的数据
+      for (const key in newForm) {
+        if (Object.hasOwnProperty.call(newForm, key)) {
+          // 如果是嵌套对象,递归更新
+          if (typeof newForm[key] === 'object' && newForm[key] !== null) {
+            for (const nestedKey in newForm[key]) {
+              this.$set(this.list[this.index][key], nestedKey, newForm[key][nestedKey])
+            }
+          } else {
+            this.$set(this.list[this.index], key, newForm[key])
+          }
+        }
+      }
+    },
+    /** 处理左侧组件拖拽开始 */
+    handleDragStart(event, comp) {
+      this.draggedComponent = comp
+      this.insertIndex = -1
+      event.dataTransfer.effectAllowed = 'copy'
+      event.dataTransfer.setData('text/html', event.target.innerHTML)
+    },
+    /** 处理拖拽结束 */
+    handleDragEnd() {
+      this.draggedComponent = null
+      this.insertIndex = -1
+    },
+    /** 处理控件池drag-enter */
+    handleItemDragEnter(event, index) {
+      // 仅根据是否来自左侧组件池
+      if (!this.draggedComponent) return
+      event.preventDefault()
+    },
+    /** 处理控件池drag-over */
+    handleItemDragOver(event, index) {
+      // 只有来自于左侧组件池的拖拽有效
+      if (!this.draggedComponent) return
+      event.preventDefault()
+      event.dataTransfer.dropEffect = 'copy'
+      
+      const wrapper = event.currentTarget
+      const rect = wrapper.getBoundingClientRect()
+      const midPoint = rect.top + rect.height / 2
+      
+      // 根据鼠标的纵位置判断是开始位置还是结数位置
+      wrapper.classList.remove('drag-before', 'drag-after')
+      if (event.clientY < midPoint) {
+        wrapper.classList.add('drag-before')
+        this.insertIndex = index
+      } else {
+        wrapper.classList.add('drag-after')
+        this.insertIndex = index + 1
+      }
+    },
+    /** 处理控件池drag-leave */
+    handleItemDragLeave(event, index) {
+      // 仅在来自左侧拖拽时事
+      if (!this.draggedComponent) return
+      const wrapper = event.currentTarget
+      if (event.target === wrapper) {
+        wrapper.classList.remove('drag-before', 'drag-after')
+      }
+    },
+    /** 根据鼠标位置计算插入索引 */
+    handleMouseMove(event) {
+      // 此方法已经不使用,位置计算变为了handleItemDragOver
+    },
+    /** 处理拖拽经过预览区域 */
+    handleDragOver(event) {
+      event.preventDefault()
+      event.dataTransfer.dropEffect = 'copy'
+      // 添加视觉反馈
+      this.$el.querySelector('.view-body').classList.add('drag-over')
+    },
+    /** 处理拖拽离开预览区域 */
+    handleDragLeave(event) {
+      if (event.target === this.$el.querySelector('.view-body')) {
+        this.$el.querySelector('.view-body').classList.remove('drag-over')
+      }
+    },
+    /** 处理拖拽放下 */
+    handleDrop(event) {
+      event.preventDefault()
+      this.$el.querySelector('.view-body').classList.remove('drag-over')
+      
+      // 清除所有拖拽样式
+      document.querySelectorAll('.draggable-wrapper').forEach(el => {
+        el.classList.remove('drag-before', 'drag-after')
+      })
+      
+      // 处理新添加的组件(来自于左侧组件池)
+      if (this.draggedComponent) {
+        const newItem = this.createNewComponent(this.draggedComponent)
+        // 在插入位置插入新控件
+        if (this.insertIndex >= 0 && this.insertIndex <= this.list.length) {
+          this.list.splice(this.insertIndex, 0, newItem)
+        } else {
+          this.list.push(newItem)
+        }
+        this.draggedComponent = null
+        this.insertIndex = -1
+      }
+    },
+    /** 根据组件贫模对象创建新控件 */
+    createNewComponent(item) {
+      let data = {
+        type: item.type,
+        active: false,
+        name: item.label,
+        fixe: false,
+        classText: ['parent-div'],
+        workUrl: ''
+      }
+      if (item.type === 'h5-text') {
+        data.content = '默认文本'
+        data.style = {
+          textAlign: 'left',
+          fontSize: 20,
+          background: '#FFF',
+          color: '#000',
+          fontWeight: 'none',
+          fontStyle: 'none',
+          textDecoration: 'none'
+        }
+      } else if (item.type === 'h5-image') {
+        data.url = null
+      } else if (item.type === 'h5-sep') {
+        data.classText = [...data.classText, 'divider']
+        data.style = {
+          height: '10px',
+          background: 'rgb(228, 231, 237)'
+        }
+      } else if (item.type === 'h5-countdown') {
+        data.days = 0
+        data.hours = 0
+        data.minutes = 0
+        data.seconds = 0
+        data.countdownMode = 1
+      } else if (item.type === 'h5-form') {
+        data.showImage = true
+        data.formImage = ''
+        data.showSubmitBtn = true
+        data.submitBtnStyle = 'text'
+        data.submitBtnColor = '#409EFF'
+        data.submitBtnText = '提交'
+        data.submitBtnImage = ''
+      } else if (item.type === 'h5-link-button') {
+        data.buttonStyle = 'text'
+        data.buttonText = '提交'
+        data.buttonColor = '#FF5A5A'
+        data.buttonTextColor = '#ffffff'
+        data.buttonImage = ''
+      } else if (item.type === 'h5-add-wechat-button') {
+        data.buttonStyle = 'text'
+        data.buttonText = '提交'
+        data.buttonColor = '#FF5A5A'
+        data.buttonTextColor = '#ffffff'
+        data.buttonImage = ''
+        data.enableGetCustomerAssistant = true
+        data.followScreen = true
+        data.enableDefaultWechat = false
+        data.enableMiniProgram = false
+      } else if (item.type === 'h5-qrcode') {
+        data.qrcodeImage = ''
+        data.qrcodeSize = 140
+        data.showCopyBtn = true
+        data.copyBtnText = '复制并添加老师'
+        data.copyBtnColor = '#FF9500'
+        data.copyBtnTextColor = '#ffffff'
+        data.showClickBtn = true
+        data.clickBtnText = '点击添加老师'
+        data.clickBtnColor = '#FF9500'
+        data.clickBtnTextColor = '#ffffff'
+        data.clickBtnWechatJump = false
+        data.enableGetCustomerAssistant = false
+        data.enableDefaultWechat = false
+        data.enableMiniProgram = false
+      }
+      return data
+    },
+    select(index) {
+      this.index = index
+      // 更新所有元素的active状态
+      this.list.forEach((item, i) => {
+        this.$set(item, 'active', i === index)
+      })
+      // 直接绑定list中的对象
+      this.form = this.list[index]
+    },
     del() {
-      this.list.splice(this.index, 1)
+      if (this.index >= 0 && this.index < this.list.length) {
+        this.list.splice(this.index, 1)
+        this.form = {}
+        this.index = -1
+      }
+    },
+    /** 处理删除操作 */
+    handleDelete(index) {
+      this.index = index
+      this.list.splice(index, 1)
       this.form = {}
+      this.index = -1
     },
     updateForm(newForm) {
       // 更新表单数据
-      if (this.index === undefined || !this.list[this.index]) {
-        console.error('Invalid index or list item')
+      if (this.index < 0 || this.index >= this.list.length) {
         return
       }
 
-      console.log('newForm:', newForm) // 检查 newForm 数据
       Object.assign(this.form, newForm)
 
       // 更新列表中的数据
@@ -304,102 +534,366 @@ export default {
           }
         }
       }
-
-      console.log('Updated list:', this.list[this.index])
     }
   }
 }
 </script>
 <style lang="scss" scoped>
-.button-body {
-  text-align: center;
+.h5-editor-container {
+  width: 100%;
+  height: 100%;
+  background: #f5f7fa;
+  display: flex;
+  flex-direction: column;
+}
+
+.editor-wrapper {
+  height: 100%;
+  padding: 0;
+  margin: 0 !important;
+  display: flex !important;
+}
+
+// 面板公共样式
+.panel-header {
+  padding: 16px;
+  background: #fff;
+  border-bottom: 1px solid #e8e8e8;
+  margin-bottom: 12px;
+  border-radius: 4px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  flex-shrink: 0;
+
+  h3 {
+    margin: 0;
+    font-size: 14px;
+    font-weight: 600;
+    color: #303133;
+  }
+
+  .device-info {
+    font-size: 12px;
+    color: #909399;
+  }
+}
+
+// 左侧组件面板
+.left-panel {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background: #fff;
+  border-radius: 4px 0 0 4px;
+  overflow: hidden;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+
+  .components-list {
+    flex: 1;
+    overflow-y: auto;
+    padding: 12px;
+
+    &::-webkit-scrollbar {
+      width: 6px;
+    }
+
+    &::-webkit-scrollbar-thumb {
+      background: #d0d0d0;
+      border-radius: 3px;
+
+      &:hover {
+        background: #aaa;
+      }
+    }
+  }
+}
+
+.component-group {
+  margin-bottom: 16px;
+
+  .group-title {
+    font-size: 12px;
+    font-weight: 600;
+    color: #606266;
+    padding: 8px 0;
+    margin-bottom: 12px;
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    flex-shrink: 0;
+
+    i {
+      font-size: 14px;
+      color: #409eff;
+    }
+  }
+
+  .buttons-wrapper {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: 12px;
+  }
+}
+
+.comp-button {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 16px 12px;
+  font-size: 12px;
+  border: 1px solid #e8e8e8;
+  background: #f9f9f9;
+  color: #606266;
+  transition: all 0.3s ease;
+  border-radius: 6px;
+  cursor: grab;
+  min-height: 80px;
+  gap: 8px;
+
+  i {
+    font-size: 24px;
+    flex-shrink: 0;
+    color: #409eff;
+  }
+
+  span {
+    text-align: center;
+    word-break: break-word;
+    white-space: normal;
+  }
+
+  &:hover {
+    background: #e6f2ff;
+    border-color: #409eff;
+    color: #409eff;
+    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
+    cursor: grab;
+  }
+
+  &:active {
+    background: #d3e8ff;
+    transform: scale(0.98);
+    cursor: grabbing;
+  }
+}
+
+// 中间预览面板
+.center-panel {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background: #fff;
+  border-radius: 0;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+
+.view-body {
+  width: 375px;
+  height: auto;
+  background: #fff;
+  position: relative;
+  padding: 0;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  overflow: hidden;
+  min-height: 500px;
+  border: 2px solid #e8e8e8;
+  transition: box-shadow 0.3s ease, border-color 0.3s ease, background-color 0.3s ease;
   margin: 0 auto;
 
-  .button-tag:first-child {
-    margin-top: 0 !important;
+  &:hover {
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
   }
 
-  .button-tag {
-    margin-left: 0;
-    margin-top: 10px;
+  &.drag-over {
+    border-color: #409eff;
+    background-color: #f0f9ff;
+    box-shadow: 0 2px 12px rgba(64, 158, 255, 0.3);
   }
 }
 
-/* 外层容器 (父级div) */
 .parent-container {
-  background: #eff3f5;
-  padding: 40px 0;
+  flex: 1;
+  background: linear-gradient(135deg, #f5f7fa 0%, #f0f2f5 100%);
+  overflow-y: auto;
+  border: none;
+  padding: 24px 12px;
+  border-radius: 0 0 4px 0;
+  display: flex;
+  justify-content: center;
+  align-items: flex-start;
+
+  &::-webkit-scrollbar {
+    width: 6px;
+  }
+
+  &::-webkit-scrollbar-thumb {
+    background: #d0d0d0;
+    border-radius: 3px;
+
+    &:hover {
+      background: #aaa;
+    }
+  }
+}
+
+.draggable-area {
+  padding: 0;
   width: 100%;
+}
 
-  /* 关键设置 */
-  height: 80vh;
-  overflow-y: auto; /* 滚动条出在这里 */
-  border: 1px solid #DCDEE2;
+.draggable-wrapper {
+  position: relative;
+  transition: border-color 0.2s ease, background-color 0.2s ease;
+  cursor: move;
 }
-/* 定制滚动条样式 */
-.parent-container::-webkit-scrollbar {
-  width: 6px; /* 滚动条宽度 */
+
+.draggable-wrapper.drag-before {
+  border-top: 3px solid #409eff;
 }
 
-.parent-container::-webkit-scrollbar-track {
-  background: #f1f1f1; /* 滚动条轨道背景色 */
-  border-radius: 3px; /* 轨道圆角 */
+.draggable-wrapper.drag-after {
+  border-bottom: 3px solid #409eff;
 }
 
-.parent-container::-webkit-scrollbar-thumb {
-  background: #888; /* 滚动条滑块颜色 */
-  border-radius: 3px; /* 滑块圆角 */
+.view-item {
+  cursor: pointer;
+  transition: background-color 0.2s ease;
+  border: 2px solid transparent;
+  position: relative;
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  box-sizing: border-box;
+  user-select: none;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+
+  &:hover {
+    background-color: #f0f2f5;
+  }
+
+  &.active {
+    border-color: #409eff;
+    background-color: #ecf5ff;
+  }
 }
 
-.parent-container::-webkit-scrollbar-thumb:hover {
-  background: #555; /* 滑块悬停颜色 */
+.ghost {
+  opacity: 0.5;
+  background: #f0f9ff;
 }
-.view-body {
-  width: 375px; /* 明确具体值(会覆盖下面的width:100%) */
-  height: auto; /* 改掉height:100%,否则会根据父级高度计算*/
 
-  /* 清除非必须设置 */
-  overflow-y: visible; /* 保持默认 */
+.drag {
+  opacity: 0;
+}
 
-  /* 其他属性 */
-  margin: 0 auto; /* 代替外层flex的justify-content */
-  background: #fff;
+.insert-placeholder {
+  width: 100%;
+  height: 3px;
+  background: linear-gradient(to right, #409eff 0%, #409eff 100%);
   position: relative;
-  padding: 0;
+  margin: 4px 0;
+  border-radius: 2px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  span {
+    position: absolute;
+    background: #fff;
+    padding: 0 8px;
+    font-size: 10px;
+    color: #409eff;
+    font-weight: 600;
+  }
 }
 
-.icon.svg {
+.delete-button-wrapper {
+  position: absolute;
+  top: 8px;
+  right: 8px;
+  width: 28px;
+  height: 28px;
+  background: #ff4444;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
   cursor: pointer;
-  margin-left: 20px;
-  width: 20px;
-  height: 20px;
-}
+  z-index: 10;
+  transition: all 0.3s ease;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
+
+  i {
+    color: #fff;
+    font-size: 16px;
+  }
 
-.icon.svg.active {
-  color: #02ff9b;
+  &:hover {
+    background: #ff2222;
+    transform: scale(1.1);
+    box-shadow: 0 2px 8px rgba(255, 68, 68, 0.3);
+  }
+
+  &:active {
+    transform: scale(0.95);
+  }
 }
 
-.icon-span {
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 200px;
+  color: #909399;
+  font-size: 12px;
+
+  i {
+    font-size: 48px;
+    margin-bottom: 12px;
+    color: #d0d0d0;
+  }
 }
 
-.divider {
-  width: 100%;
-  background: #DCDEE2;
-  height: 10px;
+// 右侧属性面板
+.right-panel {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background: #fff;
+  border-radius: 0 4px 4px 0;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
 }
 
-.button-tag {
-  /* 新增图标样式 */
-  .el-icon {
-    margin-right: 6px; /* 图标与文本间距 */
-    font-size: 16px; /* 统一图标大小 */
-    vertical-align: -2px; /* 垂直居中补偿 */
+.properties-wrapper {
+  flex: 1;
+  overflow-y: auto;
+  padding: 12px;
+
+  &::-webkit-scrollbar {
+    width: 6px;
   }
 
-  &.is-plain .el-icon {
-    color: #666; /* 浅色主题下保持可视性 */
+  &::-webkit-scrollbar-thumb {
+    background: #d0d0d0;
+    border-radius: 3px;
+
+    &:hover {
+      background: #aaa;
+    }
   }
 }
 
+// 分割线样式
+.divider {
+  width: 100%;
+  background: #DCDEE2;
+  height: 10px;
+}
 
-</style>
+</style>

+ 76 - 49
src/views/adv/landingPageTemplate/index.vue

@@ -19,16 +19,6 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="域名" prop="domainId">
-        <el-select v-model="queryParams.domainId" placeholder="请选择域名" clearable size="small">
-          <el-option
-            v-for="item in domainOptions"
-            :key="item.id"
-            :label="item.name"
-            :value="item.id"
-          />
-        </el-select>
-      </el-form-item>
       <el-form-item label="状态" prop="status">
         <el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
           <el-option label="启用" :value="1" />
@@ -69,11 +59,6 @@
       <el-table-column label="ID" align="center" prop="id" width="200" show-overflow-tooltip />
       <el-table-column label="模板名称" align="center" prop="templateName" />
       <el-table-column label="模板类型" align="center" prop="templateType" width="120" />
-      <el-table-column label="域名" align="center" prop="domainId" width="150">
-        <template slot-scope="scope">
-          <span>{{ getDomainName(scope.row.domainId) }}</span>
-        </template>
-      </el-table-column>
       <el-table-column label="状态" align="center" prop="status" width="100">
         <template slot-scope="scope">
           <el-switch
@@ -129,25 +114,18 @@
           <el-input v-model="form.templateName" placeholder="请输入模板名称" />
         </el-form-item>
         <el-form-item label="模板类型" prop="templateType">
-          <el-input v-model="form.templateType" placeholder="请输入模板类型,如:DEFAULT" />
-        </el-form-item>
-        <el-form-item label="关联域名" prop="domainId">
-          <el-select v-model="form.domainId" placeholder="请选择域名" style="width: 100%">
-            <el-option
-              v-for="item in domainOptions"
-              :key="item.id"
-              :label="item.name"
-              :value="item.id"
-            />
+          <el-select v-model="form.templateType" placeholder="请选择模板类型" style="width: 100%">
+            <el-option label="免费表单类" value="免费表单类" />
+            <el-option label="小程序表单类" value="小程序表单类" />
+            <el-option label="一站式小程序表单类" value="一站式小程序表单类" />
+            <el-option label="免费加粉类" value="免费加粉类" />
+            <el-option label="小程序加粉类" value="小程序加粉类" />
           </el-select>
         </el-form-item>
-        <el-form-item label="模板数据" prop="templateData">
-          <el-input 
-            v-model="form.templateData" 
-            type="textarea" 
-            :rows="10"
-            placeholder="请输入模板数据(JSON格式)" 
-          />
+        <el-form-item label="编辑页面">
+          <el-button type="primary" @click="openH5(form.templateData)" :disabled="!form.templateType">
+            编辑页面
+          </el-button>
         </el-form-item>
         <el-form-item label="状态" prop="status">
           <el-radio-group v-model="form.status">
@@ -164,15 +142,30 @@
         <el-button @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+
+    <!-- H5编辑对话框 -->
+    <el-dialog title="编辑页面" :visible.sync="h5Open" append-to-body fullscreen class="h5-editor-dialog">
+      <div class="h5-editor-wrapper">
+        <H5Editor ref="h5Editor" />
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="h5Ok">确 定</el-button>
+        <el-button @click="h5Cancel">取 消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import { pageTemplate, getTemplate, addTemplate, updateTemplate, delTemplate, batchDelTemplate, updateTemplateStatus, copyTemplate } from "@/api/adv/landingPageTemplate";
 import { pageDomain } from "@/api/adv/domain";
+import H5Editor from "@/components/H5Editor/index.vue";
 
 export default {
   name: "LandingPageTemplate",
+  components: {
+    H5Editor
+  },
   data() {
     return {
       // 遮罩层
@@ -189,13 +182,12 @@ export default {
       total: 0,
       // 模板表格数据
       templateList: [],
-      // 域名选项
-      domainOptions: [],
       // 弹出层标题
       title: "",
       // 是否显示弹出层
       open: false,
-      // 查询参数
+      // H5编辑器对话框
+      h5Open: false,
       queryParams: {
         page: 1,
         size: 10,
@@ -221,17 +213,6 @@ export default {
     this.getList();
   },
   methods: {
-    /** 查询域名选项 */
-    getDomainOptions() {
-      pageDomain({ pageNum: 1, pageSize: 1000, status: 1 }).then(response => {
-        this.domainOptions = response.data.records;
-      });
-    },
-    /** 获取域名名称 */
-    getDomainName(domainId) {
-      const domain = this.domainOptions.find(item => item.id === domainId);
-      return domain ? domain.name : domainId;
-    },
     /** 查询模板列表 */
     getList() {
       this.loading = true;
@@ -267,9 +248,9 @@ export default {
         id: undefined,
         templateName: undefined,
         templateData: undefined,
-        templateType: "DEFAULT",
+        templateType: undefined,
         domainId: undefined,
-        status: 1,
+        status: 0,
         remark: undefined
       };
       this.resetForm("form");
@@ -293,7 +274,10 @@ export default {
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();
-      this.getDomainOptions(); // 在新增时调用
+      // 清空H5编辑器的缓存数据
+      if (this.$refs.h5Editor) {
+        this.$refs.h5Editor.initData("[]");
+      }
       this.open = true;
       this.title = "添加模板";
     },
@@ -367,8 +351,51 @@ export default {
         this.getList();
         this.msgSuccess("删除成功");
       }).catch(function() {});
+    },
+    /** 打开H5编辑器 */
+    openH5(templateData) {
+      this.h5Open = true;
+      this.$nextTick(() => {
+        this.$refs.h5Editor.initData(templateData || "[]");
+      });
+    },
+    /** H5编辑器确定 */
+    h5Ok() {
+      // 通过ref获取子组件的list数据
+      const listData = this.$refs.h5Editor.list;
+      this.form.templateData = JSON.stringify(listData);
+      this.h5Open = false; // 关闭对话框
+    },
+    /** H5编辑器取消 */
+    h5Cancel() {
+      this.h5Open = false;
     }
   }
 };
 </script>
 
+<style scoped>
+.h5-editor-dialog /deep/ .el-dialog {
+  display: flex;
+  flex-direction: column;
+}
+
+.h5-editor-dialog /deep/ .el-dialog__body {
+  flex: 1;
+  overflow: hidden;
+  padding: 0;
+}
+
+.h5-editor-wrapper {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.h5-editor-wrapper /deep/ .h5-editor-container {
+  width: 100%;
+  height: 100%;
+}
+</style>
+