mirror of
https://gitee.com/hoslay/ddei-editor.git
synced 2025-12-06 17:18:36 +08:00
648 lines
19 KiB
Vue
648 lines
19 KiB
Vue
<template>
|
||
<div v-if="forceRefresh" ref="propertyView"
|
||
:class="{ 'propertyview': true, 'propertyview--disabled': propertyDisabled }" @mousedown="changeEditorFocus">
|
||
<div class="propertyview-header">
|
||
<svg aria-hidden="true" v-if="expand"
|
||
:class="{ 'icon': true, 'header-7': propertyViewShow, 'header-7-expand': !propertyViewShow }"
|
||
@click="hidOrShowPV">
|
||
<use v-if="propertyViewShow" xlink:href="#icon-expand4"></use>
|
||
<use v-if="!propertyViewShow" xlink:href="#icon-expand3"></use>
|
||
</svg>
|
||
<div style="flex:1"></div>
|
||
<svg class="icon ding" aria-hidden="true" v-if="propertyViewShow">
|
||
<use xlink:href="#icon-ding"></use>
|
||
</svg>
|
||
</div>
|
||
<div class="content">
|
||
<div class="propertyview-subgroup" v-show="editor?.rightWidth > pvGroupWidth">
|
||
<div class="propertyview-subgroup-tabtitle">
|
||
<div
|
||
:class="currentTopGroup?.subGroups.length > 1 && subGroup.selected ? 'propertyview-subgroup-tabtitle-item--selected' : 'propertyview-subgroup-tabtitle-item'"
|
||
v-show="!subGroup.empty" v-for="subGroup in currentTopGroup?.subGroups" :title="subGroup.name"
|
||
@mouseup="changeSubGroup(subGroup)">{{
|
||
editor.i18n(subGroup.name) }}</div>
|
||
</div>
|
||
<div class="propertyview-subgroup-tabpanel" @mousewheel="mouseWheel($event)">
|
||
<div
|
||
:class="{ 'propertyview-subgroup-tabpanel-editors-column': attrDefine.display == 'column', 'propertyview-subgroup-tabpanel-editors-row': attrDefine.display != 'column', 'empty-value': attrDefine.value ? false : true }"
|
||
v-for="attrDefine in currentSubGroup?.children" :title="attrDefine.desc"
|
||
v-show="attrDefine?.visiable && !attrDefine?.forceHidden">
|
||
<div class="title" v-if="!attrDefine.hiddenTitle && attrDefine?.visiable != false">{{ editor.i18n(attrDefine.name)
|
||
}}<span v-if="attrDefine.notNull">*</span>:
|
||
</div>
|
||
<div class="editor" v-if="attrDefine.visiable != false">
|
||
<component :editor="editor" :controlDefine="controlDefine"
|
||
:is="editor?.getPropEditor(attrDefine.controlType)" v-if="reFresh && attrDefine?.visiable != false"
|
||
:attrDefine="attrDefine"></component>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="propertyview-groupview" ref="propertyviewGroupview">
|
||
<div class="propertyview-groupview-items">
|
||
<div
|
||
:class="topGroup.selected ? 'propertyview-groupview-items-item--selected' : 'propertyview-groupview-items-item'"
|
||
v-for="topGroup in topGroups" v-show="!topGroup?.empty" @click="changeTopGroup(topGroup)"
|
||
:title="topGroup.name">
|
||
<svg class="icon img" aria-hidden="true">
|
||
<use :xlink:href="'#' + topGroup.img"></use>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts">
|
||
import {DDeiEditorUtil} from "ddei-framework";
|
||
import {DDeiEditor} from "ddei-framework";
|
||
import { cloneDeep } from "ddei-framework";
|
||
import {DDeiUtil} from "ddei-framework";
|
||
import {DDeiAbstractShape} from "ddei-framework";
|
||
import {DDeiEditorArrtibute } from "ddei-framework";
|
||
import {DDeiEditorState} from "ddei-framework";
|
||
import {DDeiEditorEnumBusCommandType} from "ddei-framework";
|
||
import {DDeiEnumOperateType} from "ddei-framework";
|
||
export default {
|
||
name: "ddei-core-panel-propertyview",
|
||
extends: null,
|
||
mixins: [],
|
||
props: {
|
||
//外部传入的插件扩展参数
|
||
options: {
|
||
type: Object,
|
||
default: null
|
||
},
|
||
editor: {
|
||
type: DDeiEditor,
|
||
default: null,
|
||
},
|
||
//是否允许展开收折
|
||
expand:{
|
||
type: Boolean,
|
||
default: true
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
//当前被选中控件的引用
|
||
selectedModels: null,
|
||
//属性定义的引用
|
||
controlDefine: null,
|
||
//当前的顶级group
|
||
topGroups: null,
|
||
currentTopGroup: null,
|
||
currentSubGroup: null,
|
||
reFresh: true,
|
||
propertyDisabled: false,
|
||
propertyViewShow: true,
|
||
pvGroupWidth: 0,
|
||
rightRate: 0,//右边部分所占的比例
|
||
forceRefresh:true,
|
||
};
|
||
},
|
||
computed: {},
|
||
watch: {
|
||
currentSubGroup() {
|
||
this.forceRefreshSub();
|
||
},
|
||
},
|
||
components: {
|
||
|
||
},
|
||
created() {
|
||
// 监听obj对象中prop属性的变化
|
||
this.$watch("editor.ddInstance.stage.selectedModels", this.refreshAttrs);
|
||
// 监听obj对象中prop属性的变化
|
||
this.$watch("editor.maxWidth", function (newVal, oldVal) {
|
||
if (this.rightRate == 0) {
|
||
this.rightRate = this.editor.rightWidth / document.body.clientWidth
|
||
}
|
||
});
|
||
},
|
||
mounted() {
|
||
|
||
},
|
||
methods: {
|
||
|
||
//强制刷新当前以及下层组件
|
||
forceRefreshParts(parts) {
|
||
if (!parts || parts == 'property' || parts.indexOf('property') != -1) {
|
||
this.forceRefresh = false
|
||
this.$nextTick(() => {
|
||
this.forceRefresh = true;
|
||
if (this.refreshData) {
|
||
this.refreshData();
|
||
}
|
||
});
|
||
}
|
||
},
|
||
|
||
refreshData() {
|
||
this.editor.properyViewer = this;
|
||
this.refreshAttrs();
|
||
},
|
||
|
||
mouseWheel(evt) {
|
||
if (evt.currentTarget.clientHeight < evt.currentTarget.scrollHeight) {
|
||
evt.cancelBubble = true;
|
||
return false;
|
||
}
|
||
},
|
||
forceRefreshSub() {
|
||
this.reFresh = false;
|
||
this.propertyDisabled = false
|
||
this.$nextTick(() => {
|
||
this.propertyDisabled = (this.editor.state == DDeiEditorState.QUICK_EDITING)
|
||
this.reFresh = true;
|
||
});
|
||
},
|
||
|
||
refreshAttrs(newVal, oldVal) {
|
||
let firstControlDefine;
|
||
let firstModel;
|
||
if (!this.editor.currentControlDefine) {
|
||
let models: DDeiAbstractShape[] = [this.editor.ddInstance.stage];
|
||
firstModel = models[0];
|
||
this.selectedModels = models;
|
||
firstControlDefine = cloneDeep(
|
||
this.editor.controls.get(firstModel?.modelCode)
|
||
);
|
||
} else {
|
||
this.selectedModels = this.editor.ddInstance.stage.selectedModels;
|
||
if (this.selectedModels?.size > 0){
|
||
firstModel = Array.from(this.selectedModels.values());
|
||
firstControlDefine = this.editor.currentControlDefine
|
||
}
|
||
}
|
||
|
||
//获取第一个组件及其定义
|
||
if (firstControlDefine) {
|
||
let topGroups = null;
|
||
if (firstControlDefine.type == "DDeiStage") {
|
||
firstControlDefine.attrDefineMap.forEach((attrDefine, attrKey) => {
|
||
//当前属性的定义
|
||
|
||
attrDefine.value = DDeiUtil.getDataByPathList(
|
||
firstModel,
|
||
attrDefine.code,
|
||
attrDefine.mapping
|
||
);
|
||
attrDefine.model = firstModel;
|
||
});
|
||
//加载layer的配置
|
||
let layerControlDefine = cloneDeep(
|
||
this.editor.controls.get("DDeiLayer")
|
||
);
|
||
let layer = firstModel.layers[firstModel.layerIndex];
|
||
layerControlDefine.attrDefineMap.forEach((attrDefine, attrKey) => {
|
||
//当前属性的定义
|
||
|
||
attrDefine.value = DDeiUtil.getDataByPathList(
|
||
layer,
|
||
attrDefine.code,
|
||
attrDefine.mapping
|
||
);
|
||
attrDefine.model = layer;
|
||
});
|
||
firstControlDefine.groups.forEach(topGroup => {
|
||
topGroup.img = topGroup.icon
|
||
});
|
||
layerControlDefine.groups.forEach(topGroup => {
|
||
topGroup.img = topGroup.icon
|
||
});
|
||
topGroups = layerControlDefine.groups.concat(firstControlDefine.groups)
|
||
|
||
}
|
||
else {
|
||
//同步引用关系
|
||
firstControlDefine.groups.forEach(topGroup => {
|
||
topGroup.img = topGroup.icon
|
||
});
|
||
topGroups = firstControlDefine.groups
|
||
}
|
||
if (topGroups?.length > 0) {
|
||
//上一次编辑的名称
|
||
let upName = this.currentTopGroup?.name;
|
||
let currentTopGroup = null;
|
||
if (upName) {
|
||
for (let x = 0; x < firstControlDefine.groups.length; x++) {
|
||
let topGroup = topGroups[x];
|
||
if (!topGroup.empty &&
|
||
upName == topGroup.name) {
|
||
topGroup.selected = true;
|
||
currentTopGroup = topGroup;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (!currentTopGroup) {
|
||
topGroups[0].selected = true
|
||
currentTopGroup = topGroups[0]
|
||
}
|
||
this.currentTopGroup = currentTopGroup;
|
||
this.controlDefine = firstControlDefine;
|
||
this.topGroups = topGroups;
|
||
//上一次编辑的名称
|
||
let upSubGroupName = this.currentSubGroup?.name;
|
||
let currentSubGroup = null;
|
||
if (upSubGroupName) {
|
||
for (let sgi = 0; sgi < currentTopGroup?.subGroups.length; sgi++) {
|
||
if (
|
||
!currentTopGroup?.subGroups[sgi]?.empty &&
|
||
upSubGroupName == currentTopGroup?.subGroups[sgi]?.name
|
||
) {
|
||
currentSubGroup = currentTopGroup?.subGroups[sgi];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (!currentSubGroup) {
|
||
for (let sgi = 0; sgi < currentTopGroup?.subGroups.length; sgi++) {
|
||
if (!currentTopGroup?.subGroups[sgi]?.empty) {
|
||
currentSubGroup = currentTopGroup?.subGroups[sgi];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
this.changeSubGroup(currentSubGroup);
|
||
} else {
|
||
//清除信息
|
||
this.controlDefine = null;
|
||
this.topGroups = null;
|
||
if (this.currentTopGroup) {
|
||
this.currentTopGroup.subGroups = null;
|
||
}
|
||
if (this.currentSubGroup) {
|
||
this.currentSubGroup.children = null;
|
||
}
|
||
this.editor.currentControlDefine = null;
|
||
}
|
||
}
|
||
},
|
||
|
||
getFirstChildAttrsGroup(control) {
|
||
if (control.models?.size > 0) {
|
||
let firstControl = control.models.get(control.midList[0])
|
||
let curDefine = this.editor.controls.get(firstControl.modelCode);
|
||
if (curDefine.groups?.length > 0) {
|
||
let returnGroups = curDefine.groups;
|
||
returnGroups.forEach(group => {
|
||
group?.subGroups.forEach(subGroup => {
|
||
subGroup.children?.forEach(attrDefine => {
|
||
attrDefine.value = DDeiUtil.getDataByPathList(
|
||
firstControl,
|
||
attrDefine.code,
|
||
attrDefine.mapping
|
||
);
|
||
attrDefine.model = firstControl
|
||
});
|
||
});
|
||
});
|
||
return returnGroups;
|
||
} else if (firstControl.baseModelType == 'DDeiContainer' && firstControl.layout == 'compose') {
|
||
return this.getFirstChildAttrsGroup(firstControl)
|
||
}
|
||
}
|
||
return null
|
||
},
|
||
|
||
/**
|
||
* 展开顶级属性,收起其他顶级层级
|
||
*/
|
||
changeTopGroup(pData) {
|
||
if (this.currentTopGroup != pData) {
|
||
this.topGroups.forEach((group) => {
|
||
if (group != pData) {
|
||
group.selected = false;
|
||
}
|
||
});
|
||
pData.selected = true;
|
||
this.currentTopGroup = pData;
|
||
//上一次编辑的名称
|
||
let upSubGroupName = this.currentSubGroup?.name;
|
||
let currentSubGroup = null;
|
||
if (upSubGroupName) {
|
||
for (let sgi = 0; sgi < pData?.subGroups.length; sgi++) {
|
||
if (
|
||
!pData?.subGroups[sgi]?.empty &&
|
||
upSubGroupName == pData?.subGroups[sgi]?.name
|
||
) {
|
||
currentSubGroup = pData?.subGroups[sgi];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (!currentSubGroup) {
|
||
for (let sgi = 0; sgi < pData?.subGroups.length; sgi++) {
|
||
if (!pData?.subGroups[sgi]?.empty) {
|
||
currentSubGroup = pData?.subGroups[sgi];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
this.changeSubGroup(currentSubGroup);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 展开次级属性,收起其他次级层级
|
||
*/
|
||
changeSubGroup(pData) {
|
||
if (this.currentSubGroup != pData) {
|
||
this.currentTopGroup.subGroups.forEach((group) => {
|
||
if (group != pData) {
|
||
group.selected = false;
|
||
}
|
||
});
|
||
|
||
pData.selected = true;
|
||
this.selectedModels = this.editor.ddInstance.stage.selectedModels;
|
||
let models: DDeiAbstractShape[] = null;
|
||
if (this.selectedModels?.size > 0) {
|
||
//获取当前所有组件的公共属性定义
|
||
models = Array.from(this.selectedModels.values());
|
||
} else {
|
||
//获取当前所有组件的公共属性定义
|
||
models = [this.editor.ddInstance.stage];
|
||
}
|
||
pData?.children?.forEach((attd) => {
|
||
//判断当前属性是否可编辑
|
||
let rsState = DDeiUtil.invokeCallbackFunc("EVENT_CONTROL_VIEW_BEFORE", DDeiEnumOperateType.VIEW, { models: models, propName: attd?.code }, this.editor.ddInstance, null)
|
||
if (rsState == 0 || rsState == 1) {
|
||
attd.forceHidden = false;
|
||
} else {
|
||
attd.forceHidden = true;
|
||
}
|
||
});
|
||
this.currentSubGroup = pData;
|
||
}
|
||
setTimeout(() => {
|
||
this.$forceUpdate();
|
||
}, 100);
|
||
},
|
||
|
||
|
||
syncAttrsToGroup(firstControlDefine: object, pData: object): void {
|
||
let newChildren = [];
|
||
if (pData?.groups?.length > 0) {
|
||
pData?.groups?.forEach((group) => {
|
||
let newGroupChildren = [];
|
||
group.children?.forEach((curAttr: DDeiEditorArrtibute) => {
|
||
let mapObj = firstControlDefine?.attrDefineMap?.get(curAttr.code);
|
||
if (mapObj && mapObj.visiable != false) {
|
||
mapObj.topGroup = pData;
|
||
mapObj.modelCode = firstControlDefine.type;
|
||
newGroupChildren.push(mapObj);
|
||
newChildren.push(mapObj);
|
||
}
|
||
});
|
||
group.children = newGroupChildren;
|
||
if (newGroupChildren.length == 0) {
|
||
group.empty = true;
|
||
}
|
||
});
|
||
}
|
||
pData.children = newChildren;
|
||
if (newChildren.length == 0) {
|
||
pData.empty = true;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 隐藏or展示属性编辑器
|
||
*/
|
||
hidOrShowPV() {
|
||
this.propertyViewShow = !this.propertyViewShow
|
||
let pvFullWidth = document.body.clientWidth * this.rightRate;
|
||
//获取最右边区域的大小
|
||
let pvGroupViewEle = this.$refs.propertyviewGroupview
|
||
this.pvGroupWidth = pvGroupViewEle.clientWidth
|
||
if (this.editor.rightWidth > this.pvGroupWidth) {
|
||
let deltaX = pvFullWidth - this.pvGroupWidth;
|
||
this.editor.rightWidth = this.pvGroupWidth;
|
||
//重新设置画布大小
|
||
this.editor.middleWidth += deltaX;
|
||
} else {
|
||
let deltaX = pvFullWidth - this.pvGroupWidth;
|
||
|
||
this.editor.rightWidth = pvFullWidth;
|
||
//重新设置画布大小
|
||
this.editor.middleWidth -= deltaX;
|
||
}
|
||
this.editor.changeState(DDeiEditorState.DESIGNING);
|
||
},
|
||
|
||
/**
|
||
* 焦点进入当前区域
|
||
*/
|
||
changeEditorFocus() {
|
||
if (this.editor.state != DDeiEditorState.PROPERTY_EDITING && this.editor.state != DDeiEditorState.QUICK_EDITING) {
|
||
this.editor.changeState(DDeiEditorState.PROPERTY_EDITING);
|
||
}
|
||
this.editor.bus.push(DDeiEditorEnumBusCommandType.ClearTemplateUI);
|
||
this.editor.bus.executeAll();
|
||
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.propertyview {
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: var(--panel-background);
|
||
display: flex;
|
||
user-select: none;
|
||
height:100%;
|
||
|
||
&-header {
|
||
background: var(--panel-header);
|
||
border-bottom: 1px solid #D5D5DF;
|
||
flex: 0 0 30px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
|
||
|
||
.header-7 {
|
||
font-size: 18px;
|
||
margin-left: 8px;
|
||
margin-right: 4px;
|
||
}
|
||
|
||
.header-7-expand {
|
||
font-size: 18px;
|
||
margin-left: 4px;
|
||
margin-right: 4px;
|
||
}
|
||
|
||
|
||
.ding {
|
||
font-size: 20px;
|
||
margin-right: 4px;
|
||
}
|
||
|
||
}
|
||
|
||
.content {
|
||
flex: 1;
|
||
display: flex;
|
||
}
|
||
|
||
&-subgroup {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-flow: column;
|
||
&-tabtitle {
|
||
flex: 0 0 46px;
|
||
display: flex;
|
||
border-bottom: 1pt solid rgb(235, 235, 239);
|
||
color: grey;
|
||
&-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
font-size: 16px;
|
||
font-weight: 400;
|
||
color: var(--panel-title);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
&--selected {
|
||
flex: 1;
|
||
text-align: center;
|
||
font-size: 16px;
|
||
background-color: var(--background);
|
||
font-weight: 400;
|
||
color: var(--dot);
|
||
border-bottom: 4px solid var(--dot);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
&:hover {
|
||
color: var(--dot);
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
}
|
||
|
||
&-tabpanel {
|
||
text-align: center;
|
||
background: var(--panel-background);
|
||
overflow-y: auto;
|
||
display: flex;
|
||
flex-flow: column;
|
||
flex: 1 1 auto;
|
||
color: var(--panel-title);
|
||
font-size: 15px;
|
||
height: 100px;
|
||
span {
|
||
color: red;
|
||
}
|
||
&-editors-column {
|
||
display: flex;
|
||
flex-flow: column;
|
||
margin-top: 10px;
|
||
margin-bottom: 10px;
|
||
.title {
|
||
background: var(--panel-background);
|
||
color: var(--panel-title);
|
||
text-align: left;
|
||
padding-left: 10px;
|
||
margin: auto 0;
|
||
margin-bottom: 5px;
|
||
font-size: 15px;
|
||
}
|
||
.editor {
|
||
text-align: left;
|
||
padding-left: 10px;
|
||
}
|
||
}
|
||
|
||
&-editors-row {
|
||
display: flex;
|
||
margin-top: 10px;
|
||
margin-bottom: 10px;
|
||
.title {
|
||
text-align: left;
|
||
padding-left: 10px;
|
||
flex: 0 0 100px;
|
||
white-space: nowrap;
|
||
/*文字不换行*/
|
||
overflow: hidden;
|
||
/*超出部分隐藏*/
|
||
text-overflow: ellipsis;
|
||
/*溢出部分用省略号表示*/
|
||
margin: auto 0;
|
||
font-size: 15px;
|
||
}
|
||
|
||
.editor {
|
||
text-align: center;
|
||
flex: 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
.empty-value {
|
||
filter: opacity(50%);
|
||
}
|
||
|
||
&-groupview {
|
||
flex: 0 0 28px;
|
||
display: flex;
|
||
flex-flow: column;
|
||
border-left: 1px solid #E0E3E9;
|
||
&-items {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-flow: column;
|
||
&-item {
|
||
flex: 0 0 16px;
|
||
margin-top: 8px;
|
||
margin-bottom: 8px;
|
||
display: flex;
|
||
flex-flow: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
&--selected {
|
||
flex: 0 0 16px;
|
||
margin-top: 8px;
|
||
margin-bottom: 8px;
|
||
display: flex;
|
||
flex-flow: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
>svg {
|
||
color: #1F72FF;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
&--disabled {
|
||
pointer-events: none !important;
|
||
user-select: none !important;
|
||
filter: opacity(70%);
|
||
|
||
.propertyview-subgroup-tabpanel {
|
||
pointer-events: none !important;
|
||
user-select: none !important;
|
||
filter: opacity(70%) !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
</style>
|