From 35e8bd80ff9d33a7ab01777f863a41ce97a2051a Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 14 八月 2025 14:54:00 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_7004' into dev_7004
---
src/views/tool/build/index.vue | 654 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 652 insertions(+), 2 deletions(-)
diff --git a/src/views/tool/build/index.vue b/src/views/tool/build/index.vue
index c39cbd4..0895955 100644
--- a/src/views/tool/build/index.vue
+++ b/src/views/tool/build/index.vue
@@ -1,3 +1,653 @@
<template>
- <div> 琛ㄥ崟鏋勫缓 <svg-icon icon-class="build" /> </div>
-</template>
\ No newline at end of file
+ <div class="container">
+ <div class="left-board">
+ <div class="logo-wrapper">
+ <div class="logo">
+ <img :src="logo" alt="logo"> Form Generator
+ </div>
+ </div>
+ <el-scrollbar class="left-scrollbar">
+ <div class="components-list">
+ <div class="components-title">
+ <svg-icon icon-class="component" />杈撳叆鍨嬬粍浠�
+ </div>
+ <draggable class="components-draggable" :list="inputComponents"
+ :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
+ draggable=".components-item" :sort="false" @end="onEnd" item-key="label">
+ <template #item="{ element, index }">
+ <div :key="index" class="components-item" @click="addComponent(element)">
+ <div class="components-body">
+ <svg-icon :icon-class="element.tagIcon" />
+ {{ element.label }}
+ </div>
+ </div>
+ </template>
+ </draggable>
+ <div class="components-title">
+ <svg-icon icon-class="component" />閫夋嫨鍨嬬粍浠�
+ </div>
+ <draggable class="components-draggable" :list="selectComponents"
+ :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
+ draggable=".components-item" :sort="false" @end="onEnd" item-key="label">
+ <template #item="{ element, index }">
+ <div :key="index" class="components-item" @click="addComponent(element)">
+ <div class="components-body">
+ <svg-icon :icon-class="element.tagIcon" />
+ {{ element.label }}
+ </div>
+ </div>
+ </template>
+ </draggable>
+ <div class="components-title">
+ <svg-icon icon-class="component" /> 甯冨眬鍨嬬粍浠�
+ </div>
+ <draggable class="components-draggable" :list="layoutComponents"
+ :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
+ draggable=".components-item" :sort="false" @end="onEnd" item-key="label">
+ <template #item="{ element, index }">
+ <div :key="index" class="components-item" @click="addComponent(element)">
+ <div class="components-body">
+ <svg-icon :icon-class="element.tagIcon" />
+ {{ element.label }}
+ </div>
+ </div>
+ </template>
+ </draggable>
+ </div>
+ </el-scrollbar>
+ </div>
+ <div class="center-board">
+ <div class="action-bar">
+ <el-button icon="Download" type="primary" text @click="download">
+ 瀵煎嚭vue鏂囦欢
+ </el-button>
+ <el-button class="copy-btn-main" icon="DocumentCopy" type="primary" text @click="copy">
+ 澶嶅埗浠g爜
+ </el-button>
+ <el-button class="delete-btn" icon="Delete" text @click="empty" type="danger">
+ 娓呯┖
+ </el-button>
+ </div>
+ <el-scrollbar class="center-scrollbar">
+ <el-row class="center-board-row" :gutter="formConf.gutter">
+ <el-form :size="formConf.size" :label-position="formConf.labelPosition" :disabled="formConf.disabled"
+ :label-width="formConf.labelWidth + 'px'">
+ <draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup"
+ item-key="label">
+ <template #item="{ element, index }">
+ <draggable-item :key="element.renderKey" :drawing-list="drawingList" :element="element" :index="index"
+ :active-id="activeId" :form-conf="formConf" @activeItem="activeFormItem" @copyItem="drawingItemCopy"
+ @deleteItem="drawingItemDelete" />
+ </template>
+ </draggable>
+ <div v-show="!drawingList.length" class="empty-info">
+ 浠庡乏渚ф嫋鍏ユ垨鐐归�夌粍浠惰繘琛岃〃鍗曡璁�
+ </div>
+ </el-form>
+ </el-row>
+ </el-scrollbar>
+ </div>
+ <right-panel :active-data="activeData" :form-conf="formConf" :show-field="!!drawingList.length"
+ @tag-change="tagChange" />
+
+ <code-type-dialog v-model="dialogVisible" title="閫夋嫨鐢熸垚绫诲瀷" :showFileName="showFileName" @confirm="generate" />
+ <input id="copyNode" type="hidden">
+ </div>
+</template>
+
+<script setup>
+import draggable from "vuedraggable/dist/vuedraggable.common"
+import ClipboardJS from 'clipboard'
+import beautifier from 'js-beautify'
+import logo from '@/assets/logo/logo.png'
+import { inputComponents, selectComponents, layoutComponents, formConf as formConfData } from '@/utils/generator/config'
+import { beautifierConf } from '@/utils/index'
+import drawingDefalut from '@/utils/generator/drawingDefalut'
+import { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/utils/generator/html'
+import { makeUpJs } from '@/utils/generator/js'
+import { makeUpCss } from '@/utils/generator/css'
+import Download from '@/plugins/download'
+import { ElNotification } from 'element-plus'
+import DraggableItem from './DraggableItem'
+import RightPanel from './RightPanel'
+import CodeTypeDialog from './CodeTypeDialog'
+import { onMounted, watch } from 'vue'
+
+const drawingList = ref(drawingDefalut)
+const { proxy } = getCurrentInstance()
+const dialogVisible = ref(false)
+const showFileName = ref(false)
+const operationType = ref('')
+const idGlobal = ref(100)
+const activeData = ref(drawingDefalut[0])
+const activeId = ref(drawingDefalut[0].formId)
+const generateConf = ref(null)
+const formData = ref({})
+const formConf = ref(formConfData)
+let oldActiveId
+let tempActiveData
+
+function activeFormItem(element) {
+ activeData.value = element
+ activeId.value = element.formId
+}
+function copy() {
+ dialogVisible.value = true
+ showFileName.value = false
+ operationType.value = 'copy'
+}
+function download() {
+ dialogVisible.value = true
+ showFileName.value = true
+ operationType.value = 'download'
+}
+function empty() {
+ proxy.$modal.confirm('纭畾瑕佹竻绌烘墍鏈夌粍浠跺悧锛�', '鎻愮ず', { type: 'warning' }).then(() => {
+ idGlobal.value = 100
+ drawingList.value = []
+ }
+ )
+}
+
+function onEnd(obj, a) {
+ if (obj.from !== obj.to) {
+ activeData.value = tempActiveData
+ activeId.value = idGlobal.value
+ }
+}
+
+function addComponent(item) {
+ const clone = cloneComponent(item)
+ drawingList.value.push(clone)
+ activeFormItem(clone)
+}
+
+function cloneComponent(origin) {
+ const clone = JSON.parse(JSON.stringify(origin))
+ clone.formId = ++idGlobal.value
+ clone.span = formConf.value.span
+ clone.renderKey = +new Date() // 鏀瑰彉renderKey鍚庡彲浠ュ疄鐜板己鍒舵洿鏂扮粍浠�
+ if (!clone.layout) clone.layout = 'colFormItem'
+ if (clone.layout === 'colFormItem') {
+ clone.vModel = `field${idGlobal.value}`
+ clone.placeholder !== undefined && (clone.placeholder += clone.label)
+ tempActiveData = clone
+ } else if (clone.layout === 'rowFormItem') {
+ delete clone.label
+ clone.componentName = `row${idGlobal.value}`
+ clone.gutter = formConf.value.gutter
+ tempActiveData = clone
+ }
+ return tempActiveData
+}
+
+function drawingItemCopy(item, parent) {
+ let clone = JSON.parse(JSON.stringify(item))
+ clone = createIdAndKey(clone)
+ parent.push(clone)
+ activeFormItem(clone)
+}
+
+
+function createIdAndKey(item) {
+ item.formId = ++idGlobal.value
+ item.renderKey = +new Date()
+ if (item.layout === 'colFormItem') {
+ item.vModel = `field${idGlobal.value}`
+ } else if (item.layout === 'rowFormItem') {
+ item.componentName = `row${idGlobal.value}`
+ }
+ if (Array.isArray(item.children)) {
+ item.children = item.children.map(childItem => createIdAndKey(childItem))
+ }
+ return item
+}
+
+function drawingItemDelete(index, parent) {
+ parent.splice(index, 1)
+ nextTick(() => {
+ const len = drawingList.value.length
+ if (len) {
+ activeFormItem(drawingList.value[len - 1])
+ }
+ })
+}
+
+function tagChange(newTag) {
+ newTag = cloneComponent(newTag)
+ newTag.vModel = activeData.value.vModel
+ newTag.formId = activeId.value
+ newTag.span = activeData.value.span
+ delete activeData.value.tag
+ delete activeData.value.tagIcon
+ delete activeData.value.document
+ Object.keys(newTag).forEach(key => {
+ if (activeData.value[key] !== undefined
+ && typeof activeData.value[key] === typeof newTag[key]) {
+ newTag[key] = activeData.value[key]
+ }
+ })
+ activeData.value = newTag
+ updateDrawingList(newTag, drawingList.value)
+}
+
+
+function updateDrawingList(newTag, list) {
+ const index = list.findIndex(item => item.formId === activeId.value)
+ if (index > -1) {
+ list.splice(index, 1, newTag)
+ } else {
+ list.forEach(item => {
+ if (Array.isArray(item.children)) updateDrawingList(newTag, item.children)
+ })
+ }
+}
+function generate(data) {
+ generateConf.value = data
+ nextTick(() => {
+ switch (operationType.value) {
+ case 'copy':
+ execCopy(data)
+ break
+ case 'download':
+ execDownload(data)
+ break
+ default:
+ break
+ }
+ })
+}
+
+function execDownload(data) {
+ const codeStr = generateCode()
+ const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
+ Download.saveAs(blob, data.fileName)
+}
+
+function execCopy(data) {
+ document.getElementById('copyNode').click()
+}
+function AssembleFormData() {
+ formData.value = { fields: JSON.parse(JSON.stringify(drawingList.value)), ...formConf.value }
+}
+function generateCode() {
+ const { type } = generateConf.value
+ AssembleFormData()
+ const script = vueScript(makeUpJs(formData.value, type))
+ const html = vueTemplate(makeUpHtml(formData.value, type))
+ const css = cssStyle(makeUpCss(formData.value))
+ return beautifier.html(html + script + css, beautifierConf.html)
+}
+watch(() => activeData.value.label, (val, oldVal) => {
+ if (
+ activeData.value.placeholder === undefined
+ || !activeData.value.tag
+ || oldActiveId !== activeId.value
+ ) {
+ return
+ }
+ activeData.value.placeholder = activeData.value.placeholder.replace(oldVal, '') + val
+})
+watch(activeId, (val) => {
+ oldActiveId = val
+}, { immediate: true })
+
+onMounted(() => {
+ const clipboard = new ClipboardJS('#copyNode', {
+ text: trigger => {
+ const codeStr = generateCode()
+ ElNotification({ title: '鎴愬姛', message: '浠g爜宸插鍒跺埌鍓垏鏉匡紝鍙矘璐淬��', type: 'success' })
+ return codeStr
+ }
+ })
+ clipboard.on('error', e => {
+ proxy.$modal.msgError('浠g爜澶嶅埗澶辫触')
+ })
+})
+</script>
+
+<style lang='scss'>
+$lighterBlue: #2C51D9;
+
+.container {
+ position: relative;
+ width: 100%;
+ background-color: var(--el-bg-color-overlay);
+ height: calc(100vh - 50px - 40px);
+ overflow: hidden;
+
+ .left-board {
+ width: 260px;
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: calc(100vh - 50px - 40px);
+
+ .logo-wrapper {
+ position: relative;
+ height: 42px;
+ border-bottom: 1px solid var(--el-border-color-extra-light);
+ box-sizing: border-box;
+
+ .logo {
+ position: absolute;
+ left: 12px;
+ top: 6px;
+ line-height: 30px;
+ color: #00afff;
+ font-weight: 600;
+ font-size: 17px;
+ white-space: nowrap;
+
+ >img {
+ width: 30px;
+ height: 30px;
+ vertical-align: top;
+ }
+
+ .github {
+ display: inline-block;
+ vertical-align: sub;
+ margin-left: 15px;
+
+ >img {
+ height: 22px;
+ }
+ }
+ }
+ }
+
+ .left-scrollbar {
+ .el-scrollbar__wrap {
+ box-sizing: border-box;
+ overflow-x: hidden !important;
+ margin-bottom: 0 !important;
+
+ .components-list {
+ padding: 8px;
+ box-sizing: border-box;
+ height: 100%;
+
+ .components-title {
+ font-size: 14px;
+ // color: #222;
+ margin: 6px 2px;
+
+ .svg-icon {
+ // color: #666;
+ font-size: 18px;
+ margin-right: 5px;
+ }
+ }
+
+ .components-draggable {
+ padding-bottom: 20px;
+
+ .components-item {
+ display: inline-block;
+ width: 48%;
+ margin: 1%;
+ transition: transform 0ms !important;
+
+ .components-body {
+ padding: 8px 10px;
+ background: var(--el-border-color-extra-light);
+ font-size: 12px;
+ cursor: move;
+ border: 1px dashed var(--el-border-color-extra-light);
+ border-radius: 3px;
+
+ .svg-icon {
+ // color: #777;
+ font-size: 15px;
+ margin-right: 5px;
+ }
+
+ &:hover {
+ border: 1px dashed #787be8;
+ color: #787be8;
+
+ .svg-icon {
+ color: #787be8;
+ }
+ }
+ }
+ }
+ }
+
+
+ }
+ }
+ }
+ }
+
+ .center-board {
+ height: calc(100vh - 50px - 40px);
+ width: auto;
+ margin: 0 350px 0 260px;
+ box-sizing: border-box;
+
+ .action-bar {
+ position: relative;
+ height: 42px;
+ padding: 0 15px;
+ box-sizing: border-box;
+ ;
+ border: 1px solid var(--el-border-color-extra-light);
+ border-top: none;
+ border-left: none;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+
+ u .delete-btn {
+ color: #F56C6C;
+ }
+ }
+
+ .center-scrollbar {
+ height: calc(100vh - 50px - 40px - 42px);
+ overflow: hidden;
+ border-left: 1px solid var(--el-border-color-extra-light);
+ border-right: 1px solid var(--el-border-color-extra-light);
+ box-sizing: border-box;
+
+ .el-scrollbar__view {
+ overflow-x: hidden;
+ }
+
+ .center-board-row {
+ padding: 12px 12px 15px 12px;
+ box-sizing: border-box;
+
+ &>.el-form {
+ // 69 = 12+15+42
+ height: calc(100vh - 50px - 40px - 69px);
+ flex: 1;
+
+ .drawing-board {
+ height: 100%;
+ position: relative;
+
+ .components-body {
+ padding: 0;
+ margin: 0;
+ font-size: 0;
+ }
+
+ .sortable-ghost {
+ position: relative;
+ display: block;
+ overflow: hidden;
+
+ &::before {
+ content: " ";
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ height: 3px;
+ background: rgb(89, 89, 223);
+ z-index: 2;
+ }
+ }
+
+ .components-item.sortable-ghost {
+ width: 100%;
+ height: 60px;
+ background: var(--el-border-color-extra-light);
+ }
+
+ .active-from-item {
+ &>.el-form-item {
+ background: var(--el-border-color-extra-light);
+ border-radius: 6px;
+ }
+
+ &>.drawing-item-copy,
+ &>.drawing-item-delete {
+ display: initial;
+ }
+
+ &>.component-name {
+ color: $lighterBlue;
+ }
+
+ .el-input__wrapper {
+ box-shadow: 0 0 0 1px var(--el-input-hover-border-color) inset;
+ }
+ }
+
+ .el-form-item {
+ margin-bottom: 15px;
+ }
+ }
+
+ .drawing-item {
+ position: relative;
+ cursor: move;
+
+ &.unfocus-bordered:not(.activeFromItem)>div:first-child {
+ border: 1px dashed #ccc;
+ }
+
+ .el-form-item {
+ padding: 12px 10px;
+ }
+ }
+
+ .drawing-row-item {
+ position: relative;
+ cursor: move;
+ box-sizing: border-box;
+ border: 1px dashed #ccc;
+ border-radius: 3px;
+ padding: 0 2px;
+ margin-bottom: 15px;
+
+ .drawing-row-item {
+ margin-bottom: 2px;
+ }
+
+ .el-col {
+ margin-top: 22px;
+ }
+
+ .el-form-item {
+ margin-bottom: 0;
+ }
+
+ .drag-wrapper {
+ min-height: 80px;
+ flex: 1;
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+ &.active-from-item {
+ border: 1px dashed $lighterBlue;
+ }
+
+ .component-name {
+ position: absolute;
+ top: 0;
+ left: 0;
+ font-size: 12px;
+ color: #bbb;
+ display: inline-block;
+ padding: 0 6px;
+ }
+ }
+
+ .drawing-item,
+ .drawing-row-item {
+ &:hover {
+ &>.el-form-item {
+ background: var(--el-border-color-extra-light);
+ border-radius: 6px;
+ }
+
+ &>.drawing-item-copy,
+ &>.drawing-item-delete {
+ display: initial;
+ }
+ }
+
+ &>.drawing-item-copy,
+ &>.drawing-item-delete {
+ display: none;
+ position: absolute;
+ top: -10px;
+ width: 22px;
+ height: 22px;
+ line-height: 22px;
+ text-align: center;
+ border-radius: 50%;
+ font-size: 12px;
+ border: 1px solid;
+ cursor: pointer;
+ z-index: 1;
+ }
+
+ &>.drawing-item-copy {
+ right: 56px;
+ border-color: $lighterBlue;
+ color: $lighterBlue;
+ background: #fff;
+
+ &:hover {
+ background: $lighterBlue;
+ color: #fff;
+ }
+ }
+
+ &>.drawing-item-delete {
+ right: 24px;
+ border-color: #F56C6C;
+ color: #F56C6C;
+ background: #fff;
+
+ &:hover {
+ background: #F56C6C;
+ color: #fff;
+ }
+ }
+ }
+
+ .empty-info {
+ position: absolute;
+ top: 46%;
+ left: 0;
+ right: 0;
+ text-align: center;
+ font-size: 18px;
+ color: #ccb1ea;
+ letter-spacing: 4px;
+ }
+
+ }
+ }
+ }
+ }
+}
+</style>
--
Gitblit v1.9.3