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>