|
|
@@ -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>
|