瀏覽代碼

看板设计暂存

wuyunfeng 2 年之前
父節點
當前提交
8e6db1b57b

+ 1 - 0
package.json

@@ -38,6 +38,7 @@
     "jszip": "3.2.1",
     "jwt-decode": "^3.1.2",
     "moment": "^2.29.4",
+    "nanoid": "^4.0.2",
     "normalize.css": "7.0.0",
     "nprogress": "0.2.0",
     "particles.js": "2.0.0",

+ 628 - 0
src/views/custom-infoboard/infoboard-designer.vue

@@ -0,0 +1,628 @@
+<template>
+    <el-container class="_fc-designer" :style="'height:'+floorContainerHeight+'px'">
+        <el-main style="height: 100%;">
+            <el-container style="height: 100%;">
+                <el-aside class="_fc-l" width="266px">
+<!--                    <template v-for="(item, index) in menuList">-->
+<!--                        <div class="_fc-l-group" :key="index">-->
+<!--                            <h4 class="_fc-l-title">{{ item.title }}</h4>-->
+<!--                            <draggable :group="{name:'default', pull:'clone',put:false}" :sort="false"-->
+<!--                                       :list="item.list">-->
+<!--                                <div class="_fc-l-item" v-for="(data, index) in item.list"-->
+<!--                                     :key="index">-->
+<!--                                    <div class="_fc-l-icon">-->
+<!--                                        <i :class="[data.icon ? (data.icon.substring(0,3) === 'el-' ? data.icon : 'fc-icon '+data.icon) : 'fc-icon icon-input']"></i>-->
+<!--                                    </div>-->
+<!--                                    <span class="_fc-l-name">{{ data.label }}</span>-->
+<!--                                </div>-->
+<!--                            </draggable>-->
+<!--                        </div>-->
+<!--                    </template>-->
+                    <draggable v-model="templateArray"  :options="tplOptions" :clone="clonedata">
+                        <div v-for="item in templateArray" :class="'item-' + item.tpl_id" class="tpl-item">
+                            <el-row>
+                                <el-col :span="24/item.tpl_id" v-for="size in item.tpl_id" :key="size" class="grid-content" :class="'bg-purple-'+size"></el-col>
+                            </el-row>
+                            <span class="text-tpl">{{ templates[item.tpl_id].title }}</span>
+                        </div>
+
+
+                    </draggable>
+
+                </el-aside>
+                <el-container class="_fc-m">
+<!--                    <el-header class="_fc-m-tools" height="45">-->
+<!--                        <slot name="handle"></slot>-->
+<!--                        <el-button type="primary" icon="fc-icon icon-preview" plain round size="mini"-->
+<!--                                   @click="previewFc">预 览-->
+<!--                        </el-button>-->
+<!--                        <el-popconfirm-->
+<!--                                title="清空后将不能恢复,确定要清空吗?"-->
+<!--                                style="margin-left: 10px;"-->
+<!--                                confirm-button-text="清空"-->
+<!--                                cancel-button-text="取消"-->
+<!--                                @confirm="clearDragRule"-->
+<!--                        >-->
+<!--                            <el-button slot="reference" type="danger" icon="fc-icon icon-delete" plain round size="mini">清 空-->
+<!--                            </el-button>-->
+<!--                        </el-popconfirm>-->
+
+<!--                    </el-header>-->
+                    <el-main style="background: #F5F5F5;padding: 20px;">
+                        <div class="_fc-m-drag">
+                            <nest-draggable :tasks="abc" @add="addItem" @delete="deleteData">
+
+                            </nest-draggable>
+
+<!--                            <draggable v-model="abc" :options="floorOptions" class="tpl-list" @add="addItem">-->
+<!--                                <div v-for="(item, index) in abc" :class="'item-' + item.tpl_id" class="floor-item">-->
+<!--                                    <nest-component :tpl-id="item.tpl_id"-->
+<!--                                                    :data="JSON.parse(JSON.stringify(item))"-->
+<!--                                    ></nest-component>-->
+<!--&lt;!&ndash;                                <component&ndash;&gt;-->
+<!--&lt;!&ndash;                                        :is="templates[item.tpl_id]"&ndash;&gt;-->
+<!--&lt;!&ndash;                                        :data="JSON.parse(JSON.stringify(item))"&ndash;&gt;-->
+<!--&lt;!&ndash;                                        is-edit&ndash;&gt;-->
+<!--&lt;!&ndash;                                        @edit-block="(...props) => {  handleEditBlock(index, ...props) }"&ndash;&gt;-->
+<!--&lt;!&ndash;                                        @edit-title="(...props) => { handleEditTitle(index, ...props) }"&ndash;&gt;-->
+<!--&lt;!&ndash;                                        @edit-tags="(...props) => { handleEditTags(index, ...props) }"&ndash;&gt;-->
+<!--&lt;!&ndash;                                ></component>&ndash;&gt;-->
+<!--                                </div>-->
+<!--                            </draggable>-->
+                        </div>
+                    </el-main>
+                </el-container>
+                <el-aside class="_fc-r" width="320px" >
+                    <el-container style="height: 100%;">
+<!--                        <el-header height="40px" class="_fc-r-tabs">-->
+<!--                            <div class="_fc-r-tab" :class="{active: activeTab==='props'}" v-if="!!activeRule || (config && config.showFormConfig === false)"-->
+<!--                                 @click="activeTab='props'">组件配置-->
+<!--                            </div>-->
+<!--                            <div class="_fc-r-tab" v-if="!config || config.showFormConfig !== false" :class="{active: activeTab==='form' && !!activeRule}"-->
+<!--                                 @click="activeTab='form'">表单配置-->
+<!--                            </div>-->
+<!--                        </el-header>-->
+<!--                        <ElMain v-show="activeTab==='form'" v-if="!config || config.showFormConfig !== false">-->
+<!--                            <component :is="FormCreate" :rule="form.rule" :option="form.option"-->
+<!--                                       :value.sync="form.value.form"></component>-->
+<!--                        </ElMain>-->
+<!--                        <ElMain v-show="activeTab==='props'" style="padding: 0 20px;"-->
+<!--                                :key="activeRule ? activeRule._id: ''">-->
+<!--                            <div>-->
+<!--                                <ElDivider v-if="showBaseRule">基础配置</ElDivider>-->
+<!--                                <component :is="FormCreate" v-show="showBaseRule" v-model="baseForm.api" :rule="baseForm.rule"-->
+<!--                                           :option="baseForm.options"-->
+<!--                                           @change="baseChange"></component>-->
+<!--                                <ElDivider>属性配置</ElDivider>-->
+<!--                                <component :is="FormCreate" v-model="propsForm.api" :rule="propsForm.rule" :option="propsForm.options"-->
+<!--                                           @change="propChange" @removeField="propRemoveField"></component>-->
+<!--                                <ElDivider v-if="showBaseRule">验证规则</ElDivider>-->
+<!--                                <component :is="FormCreate" v-show="showBaseRule" v-model="validateForm.api" :rule="validateForm.rule"-->
+<!--                                           :option="validateForm.options"-->
+<!--                                           @update:value="validateChange"></component>-->
+<!--                            </div>-->
+<!--                        </ElMain>-->
+                    </el-container>
+                </el-aside>
+<!--                <ElDialog :visible.sync="preview.state" width="800px" append-to-body>-->
+<!--                    <ViewForm :rule="preview.rule" :option="preview.option" v-if="preview.state"></ViewForm>-->
+<!--                </ElDialog>-->
+            </el-container>
+        </el-main>
+    </el-container>
+
+
+</template>
+
+<!--
+看板样式,内容设计器
+-->
+
+
+<script>
+    import Vue from 'vue'
+    import templates, {templateArray} from './templates'
+    import VueLazyload from 'vue-lazyload'
+    import draggable from 'vuedraggable'
+    import TextDisplay from "./templates/common/text-display";
+    import NestComponent from "./nest-component";
+    import NestDraggable from "./nest-draggable";
+    import { nanoid } from 'nanoid'
+    Vue.use(VueLazyload)
+    export default {
+        name: "infoboard-designer",
+        components:{NestDraggable, NestComponent, TextDisplay, draggable},
+        provide() {
+            return {
+                fcx: {
+                    active: null
+                }
+            }
+            },
+        data() {
+
+            return {
+                templates,
+                templateArray:[...templateArray],
+                props:{
+                    type:'danger',
+                    size:'small',
+                    text:'sfdf'
+                },
+                abc:[],
+                tplOptions: {
+                    group: { name: 'tplGroup', pull: 'clone', put: false },
+                    sort: false
+                },
+                tplOptions2: {
+                    group: { name: 'child', pull: 'clone', put: false },
+                    sort: false
+                },
+                floorOptions: {
+                    animation: 150,
+                    group: { name: 'tplGroup', put: true },
+                    sort: true,
+                    handle: '.handle-move'
+                },
+
+                boardInfo: {
+                    header: {
+                        tpl_id: 4, //模板id
+                        tpl_box_style: {},//标题模块样式
+                        visible: true, //是否显示模块
+                        moduleStyle: {
+                            blockHeight: 60,
+                            background:'#0081ff'
+                        },
+                        columnList: [
+                            {
+                                label: '标题',
+                                content: '内容',
+                                labelColor: '#333333',
+                                labelBgColor: '#ffffff',
+                                labelTextSize: 22,
+                                labelAlign: 'right',
+                                labelWidth: 200,
+                                contentColor: '#333333',
+
+                                contentTextSize: 22,
+                                blockWidth: 8,
+                                boardItemHisKeyVal: ''
+                            },
+                            {
+                                label: '标题',
+                                content: '内容',
+                                contentColor: '#fff',
+                                contentTextSize: 22,
+                                contentAlign:'center',
+                                blockWidth: 10
+                            }
+                            ,
+                            {
+                                label: '标题',
+                                content: '内容',
+                                labelColor: '#333333',
+                                labelBgColor: '#ffffff',
+                                labelTextSize: 22,
+                                labelAlign: 'right',
+                                labelWidth: 200,
+                                contentColor: '#fff',
+
+                                contentTextSize: 22,
+                                blockWidth: 6,
+                                boardItemHisKeyVal: ''
+                            }
+                        ]
+                    },
+                    body: {
+                        tpl_id: 5, //模板id
+                    },
+                    footer: {}
+                }
+            }
+        },
+        computed: {
+            // 楼层模板盒子样式
+            tplBoxStyle() {
+                const { sidebar } = this.$store.getters
+                let left = (sidebar.opened ? 210 : 60)
+                left = this.tplBoxShow ? left : left - 300
+                return {
+                    left: left + 'px'
+                }
+            },
+            floorContainerHeight() {
+                return  this.mainAreaHeight
+            },
+            partInfo() {
+                return this.$store.getters.organization
+            }
+        },
+        methods: {
+            /** 保存发布 */
+            handleSaveFloor() {
+                this.titleData.content_config = JSON.stringify(this.floorList)
+                API_BoardTitle.update(this.titleData.id, this.titleData).then(() => this.$message.success('保存发布成功!'))
+            },
+
+            clonedata(origin){
+                const data = JSON.parse(JSON.stringify(origin))
+                if(data.unique===''||data.unique===undefined){
+                    data.unique=nanoid(10)
+                }
+                console.log('clone',data)
+                return data
+            },
+            handleBoardItemSet(settingData) {
+                console.log(settingData)
+                const {index, target, columnIndex} = this.editOptions
+                const blockData = {...settingData}
+                target.columnList[columnIndex] = blockData
+                this.$set(this.floorList, index, target)
+            },
+            handleBoardModuleSet(settingData) {
+                const {index, blockdata} = this.editOptions
+                const moduleSetting = {...settingData}
+                blockdata.moduleStyle = moduleSetting
+                this.$set(this.floorList, index, blockdata)
+            },
+            addItem(){
+                console.log('abc',this.abc)
+                console.log('templatearray',this.templateArray)
+            },
+            /** 编辑楼层区块 */
+            handleEditBlock(index, target, columnIndex) {
+
+                const blockData = target.columnList[columnIndex]
+
+                this.settingShow = true
+                this.boardItemConfig = {...blockData}
+                // const block = target.columnList[columnIndex].blockList[blockIndex]
+                // const type = block.block_type
+                this.editOptions = {index, target, columnIndex}
+                // const blockData = JSON.parse(JSON.stringify(block))
+                // if (type === 'IMAGE') {
+                //     this.defaultImageData = blockData.block_value ? [{
+                //         url: blockData.block_value,
+                //         opt: blockData.block_opt
+                //     }] : null
+                //     this.dialogImageShow = true
+                // } else if (type === 'GOODS') {
+                //     // 填充默认数据
+                //     // this.defaultGoodsData = blockData.block_value ? [blockData.block_value.goods_id] : []
+                //     // this.dialogGoodsShow = true
+                // } else if (type === 'BRAND') {
+                //     console.log('品牌模块')
+                // }
+            },
+
+            handleModule(index, blockdata) {
+                this.boardModuleConfig = {...blockdata.moduleStyle}
+                this.editOptions = {index, blockdata}
+                this.moduleSettingShow = true
+                console.log(index, blockdata)
+            },
+
+            /** 编辑楼层标题 */
+            handleEditTitle(index, target, columnIndex) {
+                this.editOptions = {index, target, columnIndex}
+                const column = target.columnList[columnIndex]
+                const columnData = JSON.parse(JSON.stringify(column))
+                this.defaultTitleData = {
+                    text: column.title,
+                    start_color: column.titleColors[0],
+                    end_color: column.titleColors[1]
+                }
+                this.dialogTitleShow = true
+            },
+            /** 编辑楼层标签 */
+            handleEditTags(index, target, columnIndex) {
+                this.editOptions = {index, target, columnIndex}
+                const column = target.columnList[columnIndex]
+                const columnData = JSON.parse(JSON.stringify(column))
+                this.defaultTagsData = columnData.tagList
+                this.dialogTagsShow = true
+            },
+            getBoardTitle() {
+                API_BoardTitle.get(this.titleId, {}).then(
+                    res => {
+                        console.log('res', res)
+                        this.titleData = {...res}
+                        if (res.content_config !== null && res.content_config !== '') {
+                            this.floorList = JSON.parse(res.content_config)
+                            this.floorList.forEach(item => {
+                                item.columnList.forEach(col => {
+                                    if (col.boardItemHisKeyVal !== '') {
+                                        console.log('col', this.boardItems)
+                                        const boardItem = this.boardItems.filter(p => p.his_keyval === col.boardItemHisKeyVal)[0]
+                                        console.log('boardItem', boardItem)
+                                        if (boardItem !== null && boardItem !== undefined) {
+                                            col.content = boardItem.area_content
+                                            col.label = boardItem.area_label
+                                        }
+                                    }
+                                })
+                            })
+                        } else {
+                            this.floorList = []
+                        }
+                    }
+                ).catch(error => {
+                    this.$message.error(error.message)
+                })
+            },
+            deleteData(unique){
+                this.deleteArrayData(this.abc,unique)
+
+                console.log('data delete ')
+            },
+
+            deleteArrayData(data, id) {
+                let node = null
+                if (data.length > 0) {
+                    for (var i = 0; i < data.length; i++) {
+                        if (data[i].unique === id) {
+                            node = data[i]
+                            data.splice(i,1)
+                            return
+                        }
+                        if (data[i].children && data[i].children.length > 0) {
+                              this.deleteArrayData(data[i].children, id)
+                            // if (subresult !== null) {
+                            //     node = subresult
+                            //     break
+                            // }
+                        }
+                    }
+                }
+                // delete node
+            },
+
+        }
+    }
+</script>
+
+<style scoped type="text/scss">
+    ._fc-designer {
+        height: 100%;
+        min-height: 500px;
+        overflow: hidden;
+        cursor: default;
+        position: relative;
+    }
+    ._fc-m-drag,.tpl-list{
+        height: 100%;
+    }
+    .el-aside{
+        margin-bottom: 0;
+    }
+    .container {
+        min-width: 1366px;
+    }
+
+    .floor-container {
+        display: flex;
+        justify-content: space-around;
+        background-color: #E5E7EA;
+        padding: 10px;
+    }
+
+    .draggable-box {
+        position: relative;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        flex-direction: column;
+        width: 100%;
+
+    }
+
+    .header {
+        width: 1210px;
+        overflow: hidden;
+    }
+
+    @media (max-width: 1300px) {
+        .header {
+            width: 900px;
+        }
+        .container {
+            min-width: 1000px;
+        }
+    }
+
+    .draggable-box .floor {
+        width: 800px + 50px;
+        flex-shrink: 0;
+        align-items: center;
+    }
+
+    .tpl-list {
+        display: flex;
+        flex-wrap: wrap;
+        overflow: hidden;
+        width: 100%;
+        background-color: #fff;
+    }
+
+    .tpl-item {
+        display: flex;
+        width: 100%;
+        flex-direction: column;
+        justify-content: center;
+        box-sizing: border-box;
+        border-bottom: 2px solid #D9E0E7;
+        margin-bottom: 10px;
+        /*&.item-1 .img-tpl {*/
+        /*    background: url("../../../assets/pc-tpl-01.png") no-repeat;*/
+        /*    background-size: 100%;*/
+        /*}*/
+        /*&.item-2 .img-tpl {*/
+        /*    background: url("../../../assets/pc-tpl-02.png") no-repeat;*/
+        /*    background-size: 100%;*/
+        /*}*/
+        /*&.item-3 .img-tpl {*/
+        /*    background: url("../../../assets/pc-tpl-03.png") no-repeat;*/
+        /*    background-size: 100%;*/
+        /*}*/
+        /*&.item-4 .img-tpl {*/
+        /*    background: url("../../../assets/pc-tpl-04.png") no-repeat;*/
+        /*    background-size: 100%;*/
+        /*}*/
+    }
+
+    .img-tpl {
+        width: 100%;
+        min-height: 150px;
+    }
+
+    .text-tpl {
+        text-align: center;
+        margin: 5px 0;
+        color: #ACB0B9;
+        font-size: 12px;
+    }
+
+    .floor-body {
+        display: flex;
+        justify-content: center;
+        width: 100%;
+        height: 100%;
+        /*overflow-y: hidden;*/
+    }
+
+    .floor-list {
+        background-color: #E5E7EA;
+        /*min-width: 75%;*/
+        min-height: 500px;
+    }
+
+    .floor-item {
+        position: relative;
+        box-sizing: border-box;
+
+    }
+
+
+    .floor-item .panel-handle {
+        display: none;
+        position: absolute;
+        top: 0;
+        right: -25px;
+
+    }
+
+    .floor-item .panel-handle .icon-handle {
+        display: block;
+        cursor: pointer;
+        text-align: center;
+    }
+
+    .floor-item .panel-handle .svg-icon {
+        width: 25px;
+        height: 25px;
+        background-color: #fff;
+    }
+
+    .floor-item:hover .panel-handle {
+        display: block;
+    }
+
+    .floor-item:first-child .floor-layout {
+        margin-top: 0;
+    }
+
+    .tpl-box {
+        position: fixed;
+        top: 20%;
+        left: 180px;
+        z-index: 99;
+        width: 300px;
+        margin-top: (-500px - 32px + 84px) / 2;
+        border-top: 10px solid #fff;
+        box-shadow: 4px 5px 20px 0 rgba(0, 0, 0, .6);
+        transition: all ease .3s;
+    }
+
+    .tpl-box .tpl-list {
+        height: 300px;
+        overflow-y: scroll;
+    }
+
+    .tpl-box .save-btn {
+        width: 100%;
+    }
+
+    .tpl-box .tpl-btns {
+        position: absolute;
+        top: 50%;
+        right: -25px;
+        margin-top: -35px;
+        width: 25px;
+        height: 70px;
+        background-color: #fff;
+        text-align: center;
+        padding: 5px 0;
+
+    }
+
+    .tpl-box .tpl-btns .btn-item {
+        cursor: pointer;
+        padding: 5px 0;
+
+    }
+
+    .tpl-box .tpl-btns .btn-item + .btn-item {
+        margin-top: 8px;
+    }
+
+    .tpl-box .tpl-btns .btn-item:hover {
+        background-color: #46A0FC;
+        color: #fff
+    }
+
+    .bg-blue {
+        background-color: #0081ff;
+        color: #ffffff;
+    }
+
+    /deep/ .el-header {
+        line-height: 60px;
+    }
+
+    .content_title {
+        font-size: 26px
+    }
+
+    .grid-content {
+        border-radius: 4px;
+        min-height: 36px;
+    }
+
+    .el-col {
+        border-radius: 4px;
+    }
+
+    .bg-purple-1 {
+        background: #99a9bf;
+    }
+
+    .bg-purple-2 {
+        background: #d3dce6;
+    }
+
+    .bg-purple-3 {
+        background: #e5e9f2;
+    }
+
+    /deep/ .el-scrollbar__wrap {
+        overflow-x: hidden;
+        overflow-y: visible;
+        padding-bottom: 20px;
+    }
+
+    /deep/ .el-scrollbar {
+        width: 100%;
+    }
+
+</style>

+ 5 - 0
src/views/custom-infoboard/mixin.js

@@ -15,6 +15,7 @@ export default {
     components: {
         layoutItem
     },
+
     methods: {
         /** 获取颜色相关信息 */
         colors(columnIndex = 0) {
@@ -64,6 +65,10 @@ export default {
         /** 编辑标签 */
         handleEditTags(columnIndex) {
             this.$emit('edit-tags', JSON.parse(JSON.stringify(this.data)), columnIndex)
+        },
+
+        handleActive(aa){
+         console.log('aa',aa)
         }
     }
 }

+ 76 - 0
src/views/custom-infoboard/mixinnew.js

@@ -0,0 +1,76 @@
+import dragtool from './templates/DragTool'
+export default {
+    props: {
+        /** 数据 */
+        data: {
+            type: Object,
+            default: () => ({})
+        },
+        /** 是否为编辑模式 */
+        isEdit: {
+            type: Boolean,
+            default: false
+        }
+    },
+    components: {
+        dragtool
+    },
+    methods: {
+        /** 获取颜色相关信息 */
+        colors(columnIndex = 0) {
+            const _colors = this.data.columnList[columnIndex].titleColors
+            return {
+                title: `background-color: ${_colors[0]}; background-image: linear-gradient(90deg, ${_colors.join(',')});`,
+                color: (colorIndex = 0) => `color: ${_colors[colorIndex]}`
+            }
+        },
+        // /** 获取区块链接 */
+        // blockHref(block) {
+        //     const { opt_type, opt_value } = block.block_opt
+        //     switch (opt_type) {
+        //         // 链接地址
+        //         case 'URL': return opt_value
+        //         // 商品
+        //         case 'GOODS': return `/goods/${opt_value}`
+        //         // 关键字
+        //         case 'KEYWORD': return `/goods?keyword=${encodeURIComponent(opt_value)}`
+        //         // 店铺
+        //         case 'SHOP': return `/shop/${opt_value}`
+        //         // 分类
+        //         case 'CATEGORY': return `/goods?category=${opt_value}`
+        //         default: return '/'
+        //     }
+        // },
+        /** 构建空的block */
+        emptyBlock(num = 3, type) {
+            return [...new Array(num)].map(() => ({
+                block_type: type,
+                block_value: '',
+                block_opt: {
+                    opt_type: 'NONE',
+                    opt_value: ''
+                }
+            }))
+        },
+        /** 编辑区块 */
+        handleEditBlock(columnIndex, blockIndex) {
+            console.log(JSON.stringify(this.data))
+            this.$emit('edit-block', JSON.parse(JSON.stringify(this.data)), columnIndex, blockIndex)
+        },
+        /** 编辑标题 */
+        handleEditTitle(columnIndex) {
+            this.$emit('edit-title', JSON.parse(JSON.stringify(this.data)), columnIndex)
+        },
+        /** 编辑标签 */
+        handleEditTags(columnIndex) {
+            this.$emit('edit-tags', JSON.parse(JSON.stringify(this.data)), columnIndex)
+        },
+        handleActive(aa){
+            console.log('aa',aa)
+        },
+        deleteModel(unique){
+            console.log('model delete',unique)
+            this.$emit('remove',unique)
+        }
+    }
+}

+ 46 - 0
src/views/custom-infoboard/nest-component.vue

@@ -0,0 +1,46 @@
+<template>
+<component
+        :is="templates[tplId]"
+>
+<!--    <div v-for="(child,index) in data.children" :key="index">{{-->
+<!--        child.dataTpl.tpl_id}}</div>-->
+    <component v-for="(child,index) in data.children" :is="templates[child.dataTpl.tpl_id]" :data="child" :key="index">
+    </component>
+</component>
+</template>
+
+<script>
+    import templates, {templateArray} from './templates'
+    export default {
+        name: "nest-component",
+        props: {
+            data: {
+                type:Object,
+                default:()=>{}
+            },
+            tplId:{
+                required: true,
+                type: Number
+            }
+        },
+        data(){
+            return{
+                templates,
+                templateArray,
+                floorOptions: {
+                    animation: 150,
+                    group: { name: 'tplGroup', put: true },
+                    sort: true,
+                    handle: '.handle-move'
+                },
+            }
+        },
+      mounted() {
+            console.log('child',this.data.children)
+      }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 63 - 0
src/views/custom-infoboard/nest-draggable.vue

@@ -0,0 +1,63 @@
+<template>
+    <draggable class="dragArea" tag="div" :list="tasks" :options="floorOptions" @add="additem"  :clone="clonedata">
+        <div v-for="(el,index) in tasks" :key="el.unique">
+        <component   :is="templates[el.tpl_id]" :data="el" @remove="deleteModel">
+<!--            <p>{{ el.name }}</p>-->
+            <nest-draggable :tasks="el.children" @add="additem" @delete="deleteModel"/>
+        </component>
+
+        </div>
+    </draggable>
+</template>
+<script>
+    import templates, {templateArray} from './templates'
+    import draggable from "vuedraggable";
+    export default {
+        props: {
+            tasks: {
+                required: true,
+                type: Array
+            }
+        },
+        components: {
+            draggable
+        },
+        name: "nest-draggable",
+        data(){
+            return{
+                templates:{...templates},
+                templateArray,
+                floorOptions: {
+                    animation: 150,
+                    group: { name: 'tplGroup', put: true },
+                    sort: true,
+                    handle: '.handle-move'
+                },
+            }
+        },
+        mounted() {
+            console.log(this.tasks)
+        },
+        methods:{
+            additem(){
+                this.$emit('add')
+            },
+            clonedata(origin){
+                const data = JSON.parse(JSON.stringify(origin))
+                console.log('clone',data)
+                return data
+            },
+            deleteModel(unique){
+                console.log('nested delete',unique)
+                this.$emit('delete',unique)
+            }
+        }
+    };
+</script>
+<style scoped>
+    .dragArea {
+        min-height: 60px;
+        /*outline: 1px dashed;*/
+    }
+</style>
+

+ 138 - 0
src/views/custom-infoboard/templates/DragTool.vue

@@ -0,0 +1,138 @@
+<template>
+  <div class="drag-tool" @click.stop="active" :class="{active: state.active === id}" :unique="unique">
+    <div class="drag-mask" v-if="mask"></div>
+    <div class="drag-l">
+      <div class="drag-btn _fc-drag-btn active handle-move"  style="cursor: move;">
+        <i class="fc-icon el-icon-rank"></i>
+      </div>
+    </div>
+    <div class="drag-r">
+      <div class="drag-btn" @click="$emit('add')">
+        <i class="fc-icon el-icon-circle-plus-outline"></i>
+      </div>
+      <div class="drag-btn" @click="$emit('copy')">
+        <i class="fc-icon el-icon-copy-document"></i>
+      </div>
+      <div class="drag-btn" v-if="children" @click="$emit('addChild')">
+        <i class="fc-icon el-icon-circle-plus-outline"></i>
+      </div>
+      <div class="drag-btn drag-btn-danger" @click="$emit('delete',id)">
+        <i class="fc-icon el-icon-delete"></i>
+      </div>
+    </div>
+    <slot name="default" :block="block"></slot>
+  </div>
+</template>
+
+<script>
+let id = 1;
+export default {
+    name: 'DragTool',
+    props: ['dragBtn', 'children', 'unique', 'mask','block'],
+  inject: ['fcx'],
+    data() {
+        return {
+            id: this.unique || id++,
+            state: this.fcx
+        };
+    },
+    methods: {
+        active() {
+
+            if (this.state.active === this.id) return;
+            this.state.active = this.id;
+          console.log('active',this.state)
+          console.log('fcx',this.fcx)
+            this.$emit('active');
+        }
+    },
+  mounted(){
+    console.log('unique',this.unique)
+  },
+    beforeDestroy() {
+        this.state = {};
+    }
+};
+</script>
+
+<style>
+.drag-tool {
+  position: relative;
+  min-height: 20px;
+  box-sizing: border-box;
+  padding: 2px;
+  outline: 1px dashed #2E73FF;
+  overflow: hidden;
+  word-wrap: break-word;
+  word-break: break-all;
+}
+
+.drag-tool .drag-tool {
+  margin: 5px;
+}
+
+.drag-tool + .drag-tool {
+  margin-top: 5px;
+}
+
+.drag-tool.active {
+  outline: 2px solid #2E73FF;
+}
+
+.drag-tool.active > div > .drag-btn {
+  display: flex;
+}
+
+.drag-tool .drag-btn {
+  display: none;
+}
+
+.drag-r {
+  position: absolute;
+  right: 2px;
+  bottom: 2px;
+  z-index: 1904;
+}
+
+.drag-l {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 1904
+
+}
+
+.drag-btn {
+  height: 18px;
+  width: 18px;
+  color: #fff;
+  background-color: #2E73FF;
+  text-align: center;
+  line-height: 20px;
+  padding-bottom: 1px;
+  float: left;
+  cursor: pointer;
+  justify-content: center;
+}
+
+.drag-btn + .drag-btn {
+  margin-left: 2px;
+}
+
+.drag-btn-danger {
+  background-color: #FF2E2E;
+}
+
+.drag-btn i {
+  font-size: 13px;
+}
+
+.drag-mask{
+    z-index: 1900;
+    position: absolute;
+    top:0;
+    left:0;
+    right:0;
+    bottom: 0;
+;}
+</style>

+ 133 - 0
src/views/custom-infoboard/templates/common/cloumn-container.vue

@@ -0,0 +1,133 @@
+
+
+<template>
+
+
+
+            <el-col :span="8">
+                <drag-tool :unique="data.unique">
+                <slot></slot>
+                </drag-tool>
+            </el-col>
+
+
+
+</template>
+
+<script>
+    import draggable from 'vuedraggable'
+    import mixin from '../../mixin'
+    import DragTool from "../DragTool";
+    export default {
+        name: "cloumn-container",
+        components: {DragTool,draggable},
+        mixins: [mixin],
+        title: '列容器',
+        data(){
+            return{
+            }
+        },
+        dataTpl: {
+            tpl_id: 10,
+            tpl_type: 'BOARD_ITEM',
+            unique:'',
+            moduleStyle: {
+                borderBottomShow: true,
+                borderTopShow: true,
+                borderLeftShow: true,
+                borderRightShow: true,
+                borderWidth: 2,
+                borderColor: '#eeeeee',
+                blockHeight: 50
+            },
+            rows:[
+                {
+                    columnList: [
+                        {
+                            span:8,
+                            color:'#555555',
+                            fontSize:'14px',
+                            backgroundColor:'',
+                            fontFamily:'微软雅黑',
+                            field:'',
+                            text:'床位',
+                            textAlign:'',
+                        },
+                        {
+                            span:8,
+                            color:'#555555',
+                            fontSize:'14px',
+                            backgroundColor:'',
+                            fontFamily:'微软雅黑',
+                            field:'',
+                            text:'姓名',
+                            textAlign:'',
+                        },
+                        {
+                            span:8,
+                            color:'#555555',
+                            fontSize:'14px',
+                            backgroundColor:'',
+                            fontFamily:'微软雅黑',
+                            field:'',
+                            text:'病危',
+                            textAlign:'',
+                        }
+                    ]
+                }
+            ],
+
+
+            columnList: [
+                {
+                    label: '标题',
+                    content: '内容',
+                    labelColor: '#333333',
+                    labelBgColor: '#ffffff',
+                    labelTextSize:22,
+                    labelAlign: 'right',
+                    labelWidth: 200,
+                    contentColor: '#333333',
+                    contentBgColor: '#ffffff',
+                    contentTextSize:22,
+                    blockWidth: 8,
+                    boardItemHisKeyVal:''
+                },
+                {
+                    label: '标题',
+                    content: '内容',
+                    labelColor: '#333333',
+                    labelBgColor: '#ffffff',
+                    labelTextSize:22,
+                    labelAlign: 'right',
+                    labelWidth: 200,
+                    contentColor: '#333333',
+                    contentBgColor: '#ffffff',
+                    contentTextSize:22,
+                    blockWidth: 8,
+                    boardItemHisKeyVal:''
+                }
+                ,
+                {
+                    label: '标题',
+                    content: '内容',
+                    labelColor: '#333333',
+                    labelBgColor: '#ffffff',
+                    labelTextSize:22,
+                    labelAlign: 'right',
+                    labelWidth: 200,
+                    contentColor: '#333333',
+                    contentBgColor: '#ffffff',
+                    contentTextSize:22,
+                    blockWidth: 8,
+                    boardItemHisKeyVal:''
+                }
+            ],
+            children:[]
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 15 - 1
src/views/custom-infoboard/templates/common/index.js

@@ -1,9 +1,23 @@
 import tpl_one_cloum from './tpl-one-cloum'
 import tpl_two_cloum from './tpl-two-cloum'
 import tpl_three_cloum from './tpl-three-colum'
+import tpl_header from './tpl-header'
+import tpl_beds_grid from './tpl-beds-grid'
+import tpl_bed_unit from './tpl-bed-unit'
+import text_display from './text-display'
+import text_display2 from './text-display2'
+import row_container from './row-container'
+import column_container from './cloumn-container'
 
 export default {
     1: tpl_one_cloum,
     2: tpl_two_cloum,
-    3: tpl_three_cloum
+    3: tpl_three_cloum,
+    4:tpl_header,
+    5:tpl_beds_grid,
+    6:tpl_bed_unit,
+    7:text_display,
+    8:text_display2,
+    9:row_container,
+    10:column_container
 }

+ 129 - 0
src/views/custom-infoboard/templates/common/row-container.vue

@@ -0,0 +1,129 @@
+<template>
+
+    <div style="box-sizing: content-box;">
+        <drag-tool :unique="data.unique">
+        <el-row>
+            <slot></slot>
+        </el-row>
+        </drag-tool>
+    </div>
+
+</template>
+
+<script>
+    import draggable from 'vuedraggable'
+    import mixin from '../../mixin'
+    import DragTool from "../DragTool";
+    export default {
+        name: "row-container",
+        components: {DragTool,draggable},
+        mixins: [mixin],
+        title: '行容器',
+        data(){
+            return{
+            }
+        },
+        dataTpl: {
+            tpl_id: 9,
+            tpl_type: 'BOARD_ITEM',
+            unique:'',
+            moduleStyle: {
+                borderBottomShow: true,
+                borderTopShow: true,
+                borderLeftShow: true,
+                borderRightShow: true,
+                borderWidth: 2,
+                borderColor: '#eeeeee',
+                blockHeight: 50
+            },
+            rows:[
+                {
+                    columnList: [
+                        {
+                            span:8,
+                            color:'#555555',
+                            fontSize:'14px',
+                            backgroundColor:'',
+                            fontFamily:'微软雅黑',
+                            field:'',
+                            text:'床位',
+                            textAlign:'',
+                        },
+                        {
+                            span:8,
+                            color:'#555555',
+                            fontSize:'14px',
+                            backgroundColor:'',
+                            fontFamily:'微软雅黑',
+                            field:'',
+                            text:'姓名',
+                            textAlign:'',
+                        },
+                        {
+                            span:8,
+                            color:'#555555',
+                            fontSize:'14px',
+                            backgroundColor:'',
+                            fontFamily:'微软雅黑',
+                            field:'',
+                            text:'病危',
+                            textAlign:'',
+                        }
+                    ]
+                }
+            ],
+
+
+            columnList: [
+                {
+                    label: '标题',
+                    content: '内容',
+                    labelColor: '#333333',
+                    labelBgColor: '#ffffff',
+                    labelTextSize:22,
+                    labelAlign: 'right',
+                    labelWidth: 200,
+                    contentColor: '#333333',
+                    contentBgColor: '#ffffff',
+                    contentTextSize:22,
+                    blockWidth: 8,
+                    boardItemHisKeyVal:''
+                },
+                {
+                    label: '标题',
+                    content: '内容',
+                    labelColor: '#333333',
+                    labelBgColor: '#ffffff',
+                    labelTextSize:22,
+                    labelAlign: 'right',
+                    labelWidth: 200,
+                    contentColor: '#333333',
+                    contentBgColor: '#ffffff',
+                    contentTextSize:22,
+                    blockWidth: 8,
+                    boardItemHisKeyVal:''
+                }
+                ,
+                {
+                    label: '标题',
+                    content: '内容',
+                    labelColor: '#333333',
+                    labelBgColor: '#ffffff',
+                    labelTextSize:22,
+                    labelAlign: 'right',
+                    labelWidth: 200,
+                    contentColor: '#333333',
+                    contentBgColor: '#ffffff',
+                    contentTextSize:22,
+                    blockWidth: 8,
+                    boardItemHisKeyVal:''
+                }
+            ],
+            children:[]
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 44 - 0
src/views/custom-infoboard/templates/common/text-display.vue

@@ -0,0 +1,44 @@
+<template>
+
+    <drag-tool @active="handleActive" :unique="data.unique" @delete="deleteModel">
+        <div style="width:200px;">
+    <span :style="data.labelStyle">
+      标签
+    </span>
+    <span :style="data.textStyle">
+      文字
+    </span>
+
+    </div>
+    </drag-tool>
+
+    </template>
+
+    <script>
+    import DragTool from "../DragTool";
+    import mixin from '../../mixinnew'
+    export default {
+        name: "text-display",
+        mixins:[mixin],
+        components: {DragTool},
+        title:'显示单元',
+        unique:'',
+        dataTpl:{
+            tpl_id: 7,
+            tpl_type: 'BOARD_ITEM',
+            templateName:'text-display',
+            labelStyle:{
+
+            },
+            textStyle:{
+
+            },
+            children:[]
+        },
+
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 44 - 0
src/views/custom-infoboard/templates/common/text-display2.vue

@@ -0,0 +1,44 @@
+<template>
+
+    <drag-tool @active="handleActive" :unique="data.unique">
+        <div style="width:200px;">
+    <span :style="data.labelStyle">
+      标签2
+    </span>
+    <span :style="data.textStyle">
+      文字2
+    </span>
+
+    </div>
+    </drag-tool>
+
+    </template>
+
+    <script>
+    import DragTool from "../DragTool";
+    import mixin from '../../mixinnew'
+    export default {
+        name: "text-display2",
+        mixins:[mixin],
+        components: {DragTool},
+        title:'显示单元2',
+        dataTpl:{
+            tpl_id: 8,
+            tpl_type: 'BOARD_ITEM',
+            templateName:'text-display2',
+            unique:'',
+            labelStyle:{
+
+            },
+            textStyle:{
+
+            },
+            children:([])
+        },
+
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 92 - 0
src/views/custom-infoboard/templates/common/tpl-bed-unit.vue

@@ -0,0 +1,92 @@
+<template>
+
+        <div style="box-sizing: content-box;">
+            <drag-tool>
+
+             <slot></slot>
+
+            </drag-tool>
+        </div>
+
+</template>
+
+<script>
+    import draggable from 'vuedraggable'
+    import mixin from '../../mixin'
+    import DragTool from "../DragTool";
+    export default {
+        name: "tpl-bed-unit",
+        components: {DragTool,draggable},
+        mixins: [mixin],
+        title: '床位单元格',
+        data(){
+            return{
+            }
+        },
+        dataTpl: {
+            tpl_id: 6,
+            tpl_type: 'BOARD_ITEM',
+            templateName:'tpl-bed-unit',
+            moduleStyle: {
+                borderBottomShow: true,
+                borderTopShow: true,
+                borderLeftShow: true,
+                borderRightShow: true,
+                borderWidth: 2,
+                borderColor: '#eeeeee',
+                blockHeight: 50
+            },
+
+            columnList: [
+                {
+                    label: '标题',
+                    content: '内容',
+                    labelColor: '#333333',
+                    labelBgColor: '#ffffff',
+                    labelTextSize:22,
+                    labelAlign: 'right',
+                    labelWidth: 200,
+                    contentColor: '#333333',
+                    contentBgColor: '#ffffff',
+                    contentTextSize:22,
+                    blockWidth: 8,
+                    boardItemHisKeyVal:''
+                },
+                {
+                    label: '标题',
+                    content: '内容',
+                    labelColor: '#333333',
+                    labelBgColor: '#ffffff',
+                    labelTextSize:22,
+                    labelAlign: 'right',
+                    labelWidth: 200,
+                    contentColor: '#333333',
+                    contentBgColor: '#ffffff',
+                    contentTextSize:22,
+                    blockWidth: 8,
+                    boardItemHisKeyVal:''
+                }
+                ,
+                {
+                    label: '标题',
+                    content: '内容',
+                    labelColor: '#333333',
+                    labelBgColor: '#ffffff',
+                    labelTextSize:22,
+                    labelAlign: 'right',
+                    labelWidth: 200,
+                    contentColor: '#333333',
+                    contentBgColor: '#ffffff',
+                    contentTextSize:22,
+                    blockWidth: 8,
+                    boardItemHisKeyVal:''
+                }
+            ],
+            children:([])
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

File diff suppressed because it is too large
+ 473 - 0
src/views/custom-infoboard/templates/common/tpl-beds-grid.vue


File diff suppressed because it is too large
+ 473 - 0
src/views/custom-infoboard/templates/common/tpl-header.vue


+ 2 - 1
src/views/custom-infoboard/templates/common/tpl-one-cloum.vue

@@ -73,7 +73,8 @@
                     blockWidth: 24,
                     boardItemHisKeyVal:''
                 }
-            ]
+            ],
+            children:([])
         }
     }
 </script>