github.com/easysoft/zendata@v0.0.0-20240513203326-705bd5a7fd67/ui/src/components/Design.vue (about)

     1  <template>
     2    <div id="design-page">
     3      <div class="container">
     4      <div class="left" :style="styl">
     5      <a-tree
     6          ref="fieldTree"
     7          class="draggable-tree"
     8          :show-line="true"
     9          :show-icon="false"
    10          :expandedKeys.sync="openKeys"
    11          :selectedKeys.sync="selectedKeys"
    12          :tree-data="treeData"
    13          :replaceFields="fieldMap"
    14          @select="onSelect"
    15          @rightClick="onRightClick"
    16          :draggable="true"
    17          @dragenter="onDragEnter"
    18          @drop="onDrop"
    19      />
    20      <div v-if="treeNode" :style="this.tmpStyle" class="tree-context-menu">
    21          <a-menu @click="menuClick" mode="inline" class="menu">
    22          <a-menu-item key="addNeighbor" v-if="!isRoot">
    23              <a-icon type="plus" />{{ $t('msg.design.create.brother') }}
    24          </a-menu-item>
    25          <a-menu-item key="addChild" v-if="type=='def'|| ((type=='ranges' || type=='instances') && isRoot)">
    26              <a-icon type="plus" />{{ $t('msg.design.create.child') }}
    27          </a-menu-item>
    28          <a-menu-item key="remove" v-if="!isRoot">
    29              <a-icon type="delete" />{{ $t('msg.design.remove.node') }}
    30          </a-menu-item>
    31          </a-menu>
    32      </div>
    33      </div>
    34  
    35      <div class="right" :style="styl">
    36      <div v-if="rightVisible">
    37  
    38          <div v-if="type=='def' || type=='instances'">
    39          <a-tabs :activeKey="tabKey" @change="onTabChange" type="card">
    40              <a-tab-pane key="info" :tab="$t('msg.info')">
    41              <div>
    42                  <field-info-component
    43                      ref="infoComp"
    44                      :type="type"
    45                      :model="modelData"
    46                      @save="onModelSave">
    47                  </field-info-component>
    48              </div>
    49              </a-tab-pane>
    50  
    51              <a-tab-pane key="range" :tab="$t('msg.range')" force-render>
    52              <div>
    53                  <field-range-component
    54                      ref="rangeComp"
    55                      :type="type"
    56                      :model="modelData"
    57                      :time2="time2">
    58                  </field-range-component>
    59              </div>
    60              </a-tab-pane>
    61  
    62              <a-tab-pane key="refer" :tab="$t('msg.reference')" force-render>
    63              <div>
    64                  <field-refer-component
    65                      ref="referComp"
    66                      :type="type"
    67                      :model="modelData"
    68                      :time2="time2">
    69                  </field-refer-component>
    70              </div>
    71              </a-tab-pane>
    72  
    73              <a-tab-pane key="preview" :tab="$t('msg.preview')" force-render>
    74              <pre class="preview-data" v-html="previewData" style="margina: 0"></pre>
    75              </a-tab-pane>
    76          </a-tabs>
    77          </div>
    78  
    79          <div v-if="type=='ranges'">
    80          <res-ranges-item-component
    81              ref="rangesItem"
    82              :model="modelData"
    83              :time="time2"
    84              @save="onModelSave">
    85          </res-ranges-item-component>
    86          </div>
    87  
    88          <div v-if="type=='config'"> <!-- no item object, show sections page -->
    89          <div class="head">
    90              <div class="title">
    91              字段编辑
    92              </div>
    93              <div class="buttons"></div>
    94          </div>
    95  
    96          <a-row>
    97              <a-col :offset="2">
    98              <field-range-component
    99                  ref="rangeComp"
   100                  :type="'config'"
   101                  :model="modelData"
   102                  :time2="time2">
   103              </field-range-component>
   104              </a-col>
   105          </a-row>
   106          </div>
   107  
   108      </div>
   109      </div>
   110  </div>
   111  
   112      <a-modal
   113          title="确认删除"
   114          :width="400"
   115          :visible="removeVisible"
   116          okText="确认"
   117          cancelText="取消"
   118          @ok="removeNode"
   119          @cancel="cancelRemove">
   120        <div>确认删除选中节点?</div>
   121      </a-modal>
   122  
   123    </div>
   124  </template>
   125  
   126  <script>
   127  import { getDefFieldTree, getDefField, createDefField, removeDefField, moveDefField,
   128           getResRangesItemTree, getResRangesItem, createResRangesItem, removeResRangesItem,
   129           getResInstancesItemTree, getResInstancesItem, createResInstancesItem, removeResInstancesItem,
   130           getResConfigItemTree,
   131  } from "../api/manage";
   132  import FieldInfoComponent from "./FieldInfo";
   133  import FieldRangeComponent from "./FieldRange";
   134  import FieldReferComponent from "./FieldRefer";
   135  import ResRangesItemComponent from "./RangesItem"
   136  import {ResTypeDef, ResTypeInstances, ResTypeRanges, ResTypeConfig} from "../api/utils";
   137  import {previewFieldData} from "../api/manage";
   138  
   139  export default {
   140    name: 'DefDesignComponent',
   141    components: {
   142      FieldInfoComponent, FieldRangeComponent, FieldReferComponent,
   143      ResRangesItemComponent,
   144    },
   145    data() {
   146      const styl = 'height: ' + (document.documentElement.clientHeight - 56) + 'px;'
   147      return {
   148        styl: styl,
   149        removeVisible: false,
   150  
   151        tabKey: 'info',
   152        rightVisible: true,
   153        modelData: {},
   154        time2: 0,
   155  
   156        previewData: '',
   157        treeData: [],
   158        nodeMap: {},
   159        openKeys: [],
   160        selectedKeys: [],
   161        targetModel: 0,
   162        treeNode: null,
   163        fieldMap: {title: 'field', key:'id', value: 'id', children: 'fields'},
   164      };
   165    },
   166    props: {
   167      type: {
   168        type: String,
   169        required: true
   170      },
   171      modelProp: {
   172        type: Object,
   173        default: () => null
   174      },
   175      time: {
   176        type: Number,
   177        default: () => 0
   178      },
   179    },
   180  
   181    computed: {
   182      isRoot () {
   183        console.log('isRoot', this.treeNode)
   184        return !this.treeNode.parentID || this.treeNode.parentID == 0 || this.treeNode.id == 0
   185      },
   186    },
   187    created () {
   188      console.log('created')
   189      this.loadTree()
   190      this.$watch('time', () => {
   191        console.log('time changed', this.time)
   192        this.loadTree()
   193      })
   194    },
   195    mounted: function () {
   196      console.log('mounted')
   197    },
   198    beforeDestroy() {
   199      console.log('beforeDestroy')
   200    },
   201  
   202    watch: {
   203      modelProp(val) {
   204        console.log("watch modelProp :", val)
   205        this.type = ResTypeDef;
   206        this.loadTree('')
   207      },
   208    },
   209  
   210    methods: {
   211      onModelSave() {
   212        console.log('onModelSave')
   213        this.$emit('save')
   214        this.loadTree(this.selectedKeys[0])
   215      },
   216      cancel() {
   217        console.log('cancel')
   218        this.$emit('cancel')
   219      },
   220  
   221      loadTree (selectedKey) {
   222        console.log('loadTree', this.modelProp)
   223        if (!this.modelProp.id)
   224          return
   225  
   226        if (this.type === ResTypeDef) {
   227          getDefFieldTree(this.modelProp.id).then(json => {
   228            console.log('getDefFieldTree', json)
   229            this.loadTreeCallback(json, selectedKey)
   230          })
   231        } else if (this.type === ResTypeRanges) {
   232          getResRangesItemTree(this.modelProp.id).then(json => {
   233            console.log('getResRangesItemTree', json)
   234            this.loadTreeCallback(json, selectedKey)
   235          })
   236        } else if (this.type === ResTypeInstances) {
   237          getResInstancesItemTree(this.modelProp.id).then(json => {
   238            console.log('getResInstancesItemTree', json)
   239            this.loadTreeCallback(json, selectedKey)
   240          })
   241        } else if (this.type === ResTypeConfig) {
   242          getResConfigItemTree(this.modelProp.id).then(json => {
   243            console.log('getResConfigItemTree', json)
   244            this.selectedKeys = [this.modelProp.id]
   245            this.loadTreeCallback(json, this.modelProp.id)
   246          })
   247        }
   248      },
   249      loadTreeCallback(json, selectedKey) {
   250        if (json.code != 1) return
   251        this.getOpenKeys(json.data)
   252        this.treeData = [json.data]
   253  
   254        if (selectedKey) {
   255          this.getModel(selectedKey)
   256          this.rightVisible = true
   257        } else {
   258          this.rightVisible = false
   259        }
   260      },
   261      getOpenKeys (node) {
   262        if (!node) return
   263  
   264        this.openKeys.push(node.id)
   265        this.nodeMap[node.id] = node
   266        if (node.fields) {
   267          node.fields.forEach((item) => {
   268            this.getOpenKeys(item)
   269          })
   270        }
   271      },
   272      onSelect (selectedKeys, e) { // selectedKeys, e:{selected: bool, selectedNodes, node, event}
   273        console.log('onSelect', selectedKeys, e.selectedNodes, e.node, e.node.eventKey)
   274        if (selectedKeys.length == 0) {
   275          selectedKeys[0] = e.node.eventKey // keep selected
   276        }
   277  
   278        const node = this.nodeMap[e.node.eventKey]
   279        console.log('node', node)
   280        if ((this.type === 'def' && node.parentID == 0) || (this.type === 'config' && node.id == 0)
   281            || (node.fields && node.fields.length > 0)) {
   282          this.rightVisible = false
   283          this.modelData = {}
   284          return
   285        } else {
   286          this.rightVisible = true
   287          this.tabKey = 'info'
   288        }
   289  
   290        this.getModel(parseInt(selectedKeys[0]))
   291      },
   292      getModel(id) {
   293        console.log('getModel', id)
   294  
   295        if (this.type === 'def') {
   296          getDefField(id).then(res => {
   297            console.log('getDefField', res)
   298            this.modelData = res.data
   299            this.time2 = Date.now() // trigger data refresh
   300          })
   301        } else if (this.type === 'ranges') {
   302          getResRangesItem(id).then(res => {
   303            console.log('getResRangesItem', res)
   304            this.modelData = res.data
   305            this.time2 = Date.now() // trigger data refresh
   306          })
   307        } else if (this.type === 'instances') {
   308          getResInstancesItem(id).then(res => {
   309            console.log('getResInstancesItem', res)
   310            this.modelData = res.data
   311            this.time2 = Date.now() // trigger data refresh
   312          })
   313        } else if (this.type === 'config') {
   314          this.modelData = {id: id}
   315          this.time2 = Date.now() // trigger data refresh
   316        }
   317      },
   318      menuClick (e) {
   319        console.log('menuClick', e, this.treeNode)
   320        this.addMode = null
   321  
   322        this.targetModel = this.treeNode.id
   323        if (e.key === 'addNeighbor') {
   324          this.addMode = 'neighbor'
   325          this.addNeighbor()
   326        } else if (e.key === 'addChild') {
   327          this.addMode = 'child'
   328          this.addChildField()
   329        }else if (e.key === 'remove') {
   330          this.removeVisible = true
   331        }
   332        this.clearMenu()
   333      },
   334      addNeighbor () {
   335        console.log('addNeighbor', this.targetModel)
   336  
   337        if (this.type === 'def') {
   338          createDefField(this.targetModel, "neighbor").then(json => {
   339            console.log('createDefField', json)
   340            this.updateCallback(json)
   341          })
   342        } else if (this.type === 'ranges') {
   343          createResRangesItem(this.modelProp.id, "neighbor").then(json => {
   344            console.log('createResRangesItem', json)
   345            this.updateCallback(json)
   346          })
   347        } else if (this.type === 'instances') {
   348          createResInstancesItem(this.modelProp.id, "neighbor").then(json => {
   349            console.log('createResInstancesItem', json)
   350            this.updateCallback(json)
   351          })
   352        }
   353      },
   354      addChildField () {
   355        console.log('addChildField', this.targetModel)
   356  
   357        if (this.type === 'def') {
   358          createDefField(this.targetModel, "child").then(json => {
   359            console.log('createDefField', json)
   360            this.updateCallback(json)
   361          })
   362        } else if (this.type === 'ranges') {
   363          createResRangesItem(this.modelProp.id, "child").then(json => {
   364            console.log('createResRangesItem', json)
   365            this.updateCallback(json)
   366          })
   367        } else if (this.type === 'instances') {
   368          createResInstancesItem(this.modelProp.id, "child").then(json => {
   369            console.log('createResInstancesItem', json)
   370            this.updateCallback(json)
   371          })
   372        }
   373      },
   374      updateCallback(json) {
   375        this.getOpenKeys(json.data)
   376        this.treeData = [json.data]
   377  
   378        this.selectedKeys = [json.model.id] // select
   379        this.modelData = json.model
   380  
   381        this.rightVisible = true
   382      },
   383      removeNode () {
   384        console.log('removeNode', this.targetModel)
   385        this.removeVisible = false
   386        if (this.type === 'def') {
   387          removeDefField(this.targetModel).then(json => {
   388            console.log('removeDefField', json)
   389            this.removeCallback(json)
   390          })
   391        } else if (this.type === 'ranges') {
   392          removeResRangesItem(this.targetModel, this.modelProp.id).then(json => {
   393            console.log('removeResRangesItem', json)
   394            this.removeCallback(json)
   395          })
   396        } else if (this.type === 'instances') {
   397          removeResInstancesItem(this.targetModel, this.modelProp.id).then(json => {
   398            console.log('removeResInstancesItem', json)
   399            this.removeCallback(json)
   400          })
   401        }
   402      },
   403      removeCallback(json) {
   404        this.getOpenKeys(json.data)
   405        this.treeData = [json.data]
   406  
   407        this.rightVisible = false
   408      },
   409  
   410      cancelRemove (e) {
   411        e.preventDefault()
   412        this.removeVisible = false
   413      },
   414      onDragEnter(info) {
   415        console.log(info);
   416        // expandedKeys 需要受控时设置
   417        this.expandedKeys = info.expandedKeys
   418      },
   419      onDrop(info) {
   420        console.log(info, info.dragNode.eventKey, info.node.eventKey, info.dropPosition);
   421  
   422        moveDefField(info.dragNode.eventKey, info.node.eventKey, info.dropPosition).then(res => {
   423          this.getOpenKeys(res.data)
   424          this.treeData = [res.data]
   425  
   426          this.selectedKeys = [res.model.id] // select
   427          this.modelData = res.model
   428  
   429          this.rightVisible = true
   430        })
   431      },
   432  
   433      onRightClick ({ event, node }) {
   434        event.preventDefault()
   435        console.log('onRightClick', node)
   436  
   437        const y = event.currentTarget.getBoundingClientRect().top
   438        const x = event.currentTarget.getBoundingClientRect().right
   439  
   440        this.treeNode = {
   441          pageX: x,
   442          pageY: y,
   443          id: node._props.eventKey,
   444          title: node._props.title,
   445          parentID: node._props.dataRef.parentID || null
   446        }
   447  
   448        this.tmpStyle = {
   449          position: 'fixed',
   450          maxHeight: 40,
   451          textAlign: 'center',
   452          left: `${x + 10 - 0}px`,
   453          top: `${y + 6 - 0}px`
   454          // display: 'flex',
   455          // flexDirection: 'row'
   456        }
   457      },
   458      clearMenu () {
   459        console.log('clearMenu')
   460        this.treeNode = null
   461      },
   462      onTabChange(activeKey) {
   463        console.log('onTabChange', activeKey)
   464        this.tabKey = activeKey
   465  
   466        if (this.tabKey === 'preview') {
   467          console.log(111, this.getFieldTreeName(this.modelData))
   468          previewFieldData({config: this.modelProp.referName.replace(/\\/g, "/"), field: this.getFieldTreeName(this.modelData), format: 'txt'}).then(data => {
   469            console.log('previewFieldData', data)
   470            this.previewData = data
   471          })
   472        }
   473      },
   474      getFieldTreeName(node, name = ''){
   475        if(node.parentID == 0){
   476          return name;
   477        }
   478        if(this.nodeMap[node.parentID] == undefined){
   479          return name == '' ? node.field : node.field + '~~' + name;
   480        }
   481  
   482        name = name == '' ? node.field : node.field + '~~' + name;
   483  
   484        return this.getFieldTreeName(this.nodeMap[node.parentID], name)
   485      }
   486    }
   487  }
   488  </script>
   489  
   490  <style lang="less" scoped>
   491  .container {
   492    display: flex;
   493  
   494    .left {
   495      padding: 6px;
   496      width: 220px;
   497      height: 100%;
   498      border-right: 1px solid #e9f2fb;
   499      overflow: auto;
   500    }
   501    .right {
   502      flex: 1;
   503      height: 100%;
   504      padding: 6px;
   505      overflow: auto;
   506    }
   507  }
   508  
   509  .tree-context-menu {
   510    z-index: 9;
   511    .ant-tree-node-content-wrapper {
   512      display: block !important;
   513    }
   514    .menu {
   515      border: 1px solid #ebedf0;
   516      background: #f0f2f5;
   517      .ant-menu-item {
   518        padding-left: 12px !important;
   519        height: 22px;
   520        line-height: 21px;
   521        text-align: left;
   522      }
   523    }
   524  }
   525  
   526  .preview-data {
   527    padding: 5px 20px;
   528  }
   529  
   530  </style>