Commit d85eacbb authored by 张晨辰's avatar 张晨辰

feat: 属性面板

parents f407bbb9 26881ed0
......@@ -7,9 +7,6 @@
</template>
<style>
.zero-custom-text {
font-size: 14px;
}
.zero-custom-text.placeholder:after {
content: '请输入';
font-style: italic;
......
......@@ -31,13 +31,10 @@ export default {
},
data() {
return {
selected: '',
selected: this.value || '',
optionMapper
};
},
created() {
this.selected = this.value;
},
computed: {
options() {
return this.optionList || this.optionMapper[this.optionType];
......@@ -49,10 +46,6 @@ export default {
watch: {
selected(v) {
this.$emit('change', v);
},
value(v) {
console.log('selector value change', v);
this.selected = this.value;
}
}
};
......
<template>
<el-slider class="zero-slider" v-model="swvalue" :min="min" :max="max" :step="step"></el-slider>
<el-slider class="zero-slider" v-model="swvalue" :min="min" :max="max" :step="step" show-input :show-input-controls="false" input-size="mini"></el-slider>
</template>
<style>
/* .zero-slider {
.el-slider.zero-slider {
width: 180px;
}
.zero-slider .el-slider__runway.show-input {
width: 100px;
}
.zero-slider > .el-slider__input {
width: 60px;
}
/* .zero-slider > .el-slider__input > span {
display: none;
}
.zero-slider > .el-slider__input input {
padding: 0 10px;
} */
/* .zero-slider {
position: relative;
}
.zero-slider > .el-slider__input {
......@@ -11,9 +26,7 @@
top: -250%;
width: 60px;
}
.zero-slider > .el-slider__input > span {
display: none;
}
.zero-slider > .el-slider__input input {
padding: 0 10px;
} */
......@@ -28,13 +41,11 @@ export default {
},
data() {
return {
swvalue: 0
swvalue: this.value || 0
};
},
created() {
mounted() {
console.log('slider created', this.value);
this.swvalue = this.value;
},
watch: {
swvalue(v) {
......
<template>
<el-switch v-model="swvalue"></el-switch>
</template>
<style>
</style>
<script>
export default {
props: {
value: [String, Number, Boolean],
},
data() {
return {
swvalue: true
};
},
mounted() {
console.log('switch created', this.swvalue);
},
watch: {
swvalue(v) {
console.log('switch value change', this.swvalue);
this.$emit('change', v);
}
}
};
</script>
......@@ -2,7 +2,7 @@
* Created by rockyl on 2019-09-19.
*/
export const API_HOST = 'http://10.10.94.31:7777';
export const API_HOST = 'http://10.10.93.73:7777';
//export const API_HOST = 'http://localhost:3002';
export const UPLOAD_FILE_URL = API_HOST + '/api/uploadFile';
......
......@@ -4,9 +4,10 @@
import Vue from "vue";
import { projectApi } from "../../api";
import { compoleteComponentData } from '../../utils/compoleteCmpData';
// import { compoleteComponentData } from '../../utils/compoleteCmpData';
import path from "path";
import generateUUID from "uuid/v4";
import { getCmpProps } from '../../utils/common';
export const projectStore = {
state: {
......@@ -44,7 +45,7 @@ export const projectStore = {
if (!localData.dataMapping || localData.dataMapping.length === 0) {
Vue.set(localData, 'dataMapping', dataMapping || []);
}
}else{
} else {
Vue.set(localData, 'views', []);
Vue.set(localData, 'assets', []);
Vue.set(localData, 'dataMapping', []);
......@@ -53,15 +54,10 @@ export const projectStore = {
},
/**
* 激活组件
* @param {*} state
* @param {*} id
* @param {*} state
* @param {*} id
*/
activeComponent(state, item) {
// todo drag
// if (state.cmpListDragging) {
// state.cmpListDragging = false;
// return;
// }
if (item !== state.activeComponent) {
state.activeComponent = item || state.activeComponent;
}
......@@ -79,9 +75,33 @@ export const projectStore = {
if (!data || !data.label) {
return;
}
state.activeComponent.properties[data.label] = data.value;
// state.activeComponent.properties[data.label] = data.value;
Vue.set(state.activeComponent.properties, data.label, data.value);
console.log('modifyProperties', state.activeComponent);
},
/**
* 修改当前组件
* @param {*} state
* @param {*} data
*/
modifyComponent(state, data) {
if (!data || !data.label) {
return;
}
Vue.set(state.activeComponent, data.label, data.value);
if (data.label === 'type') {
// 如果修改了组件类型,需要删除无用的属性
let defaultProps = Object.keys(getCmpProps(data.value));
let cmpProps = _.cloneDeep(state.activeComponent.properties);
_.forIn(cmpProps, (value, key, prop) => {
if (defaultProps.indexOf(key) === -1) {
delete prop[key];
}
});
console.log('new properties', cmpProps);
Vue.set(state.activeComponent, 'properties', cmpProps);
}
console.log('modifyComponent', state.activeComponent);
},
modifyProject(state, ) {
......@@ -90,6 +110,7 @@ export const projectStore = {
const child = {
name,
type,
uuid: generateUUID(),
};
if (node) {
if (!node.children) {
......@@ -168,15 +189,16 @@ export const projectStore = {
* 当前激活的组件
*/
activeComponent: state => {
console.log('activeComponent', state.activeComponent || {});
let _cmp = state.activeComponent || {}
console.log('activeComponent', _cmp);
return { ...(state.activeComponent || {}), uuid: generateUUID() }
return { ..._cmp, uuid: _cmp.uuid || generateUUID() }
},
/**
* 当前激活的组件ID
*/
activeComponentId: state => {
return (state.activeComponent || {}).id;
return (state.activeComponent || {}).uuid;
},
/**
* 扁平化所有节点
......@@ -235,16 +257,25 @@ export const projectStore = {
},
/**
* 选中节点
* @param {*} param0
* @param {*} data
* @param {*} param0
* @param {*} data
*/
activeComponent({ commit }, data) {
console.log('actions activeComponent', data);
commit('activeComponent', data);
},
/**
* 修改属性
*/
modifyProperties({ commit }, data) {
commit('modifyProperties', data)
},
/**
* 修改组件
*/
modifyComponent({ commit }, data) {
commit('modifyComponent', data)
}
},
......
import _ from 'lodash';
import properties from './properties';
export const componentsMap = [{
label: '文本',
value: 'label'
......@@ -12,13 +15,127 @@ export const componentsMap = [{
value: 'rect'
}];
const isClient = typeof window !== 'undefined';
function changeCamle(s) {
return s ? s.replace(/([A-Z])/g, '-$1').toLowerCase() : '';
}
function invalidAttr(key, value) {
return !key || typeof value === 'undefined' || value === '';
}
// 属性简称 对照表
const attrShortMapper = {
align: 'text-align',
size: 'font-size',
alpha: 'opacity',
'strokeColor': 'border-color',
'strokeWidth': 'border-width',
'fillColor': 'background-color',
'source': 'background-image'
};
// 属性单位 对照表, 如果是数值的时候需要添加单位
const attrUnitMapper = {
top: 'px',
left: 'px',
width: 'px',
height: 'px',
fontSize: 'px',
size: 'px',
letterSpacing: 'px',
borderRadius: 'px',
borderWidth: 'px',
strokeWidth: 'px',
minHeight: 'px'
};
export const styles = {
getStyles() {
getStyles(value, key) {
const attr = attrShortMapper[key] || changeCamle(key);
const unit = attrUnitMapper[key] || '';
if (invalidAttr(key, value)) return '';
switch (attr) {
case 'rotate':
return ['transform', [`rotate(${value}deg)`]]; //`transform: rotate(${value}deg);`;
case 'background-image':
return ['background-image', [`url(${value})`]]; //`background-image: url(${value});background-position:center;background-size:contain;`;
case 'scale-x':
return ['transform', [`scaleX(${value})`]]; //`transform: scaleX(${value});`;
case 'scale-y':
return ['transform', [`scaleY(${value})`]]; //`transform: scaleY(${value});`;
case 'visible':
return ['display', [value ? 'block' : 'none']]; // `display: ${value ? 'block' : 'none'};`;
default:
return [attr, [`${value}${Number.isNaN(value) ? '' : unit}`]]; //`${attr}:${value}${Number.isNaN(value) ? '' : unit};`;
}
},
/**
* 根据Object类型的属性对象,生成dom中style属性可用的数据格式
* @param {*} obj
*/
getStylesFromObj(obj) {
let resultObj = {};
_.forIn(obj, (value, key) => {
let _style = styles.getStyles(value, key);
if (!_style) {
return;
}
if (resultObj[_style[0]]) {
resultObj[_style[0]] = resultObj[_style[0]].concat(_style[1]);
} else {
resultObj[_style[0]] = _style[1];
}
});
return resultObj;
},
getStyleObject(properties) {
console.log('getStyleObject', properties);
/**
* 根据组件数据,生成完整的style
* @param {*} component
*/
getComponentStyle(component) {
let result = '';
// 根据组件类型,获取默认属性
let defaultProps = getCmpProps(component.type);
defaultProps = _.omit(defaultProps, _.keys(component.properties));
defaultProps = _.mapValues(defaultProps, o => (o.value));
let _cmpProps = styles.getStylesFromObj(component.properties);
let _defaultProps = styles.getStylesFromObj(defaultProps);
_.forIn({
..._defaultProps,
..._cmpProps
}, (value, key) => {
result += `${key}: ${value.join(' ')};`
});
result += `background-position: center;background-size: contain;`
console.log('getComponentStyle', result);
return result;
}
}
export const getCmpProps = function (type) {
if (!type) {
return {}
} else {
let _nodeProps = _.cloneDeep(properties.node);
let _typeProps = {};
if (type !== 'node') {
_typeProps = _.cloneDeep(properties[type]);
}
let result = {
..._nodeProps,
..._typeProps
};
delete result.groupName;
return result
}
}
\ No newline at end of file
......@@ -7,17 +7,17 @@ const composedComponents = getComposedComponents();
export const compoleteComponentData = (views) => {
views.forEach(view => {
view.id = view.id || guid();
let _composedCmp = composedComponents[view.type];
view.component = _composedCmp.component.default;
let _composedProps = _.cloneDeep(_composedCmp.properties); // 组件预设的属性,Object类型
view.uuid = view.uuid || guid();
// let _composedCmp = composedComponents[view.type];
// view.component = _composedCmp.component.default;
// let _composedProps = _.cloneDeep(_composedCmp.properties); // 组件预设的属性,Object类型
let _viewProps = view.properties || {}; // 组件对象的具体属性,key:value
let _keys = Object.keys(_viewProps);
_keys.forEach(k => {
_composedProps[k].value = _viewProps[k]
});
view.properties = _composedProps;
// let _viewProps = view.properties || {}; // 组件对象的具体属性,key:value
// let _keys = Object.keys(_viewProps);
// _keys.forEach(k => {
// _composedProps[k].value = _viewProps[k]
// });
// view.properties = _composedProps;
if (view.children) {
compoleteComponentData(view.children);
......
<template>
<pane icon="el-icon-s-operation" :title="$t('panes.Inspector')">
<div class="zero-inspector-form">
<el-form ref="form" size="mini" v-if="activeComponent.name" :model="activeComponent.properties" label-width="80px">
<el-form ref="form" size="mini" v-if="activeComponent.name" :model="form" label-width="80px">
<el-form-item >
<el-button @click="testGetter">test</el-button>
</el-form-item>
<el-divider content-position="left">配置</el-divider>
<el-form-item label="名称">
<el-input v-model="activeComponent.name"></el-input>
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input>
</el-form-item>
<el-form-item label="类型">
<el-select v-model="activeComponent.type" placeholder="请选择类型">
<el-select v-model="form.type" @change="v => handleChange('type', v)" placeholder="请选择类型">
<el-option v-for="cmp in componentsMap" :key="cmp.value" :label="cmp.label" :value="cmp.value"></el-option>
</el-select>
</el-form-item>
......@@ -22,9 +22,6 @@
</template>
</el-form>
</div>
<!-- <div class="inspec-test" style="word-break: break-all;position: absolute;bottom: 0;font-size: 12px;background-color:#fff; width: 100%;height: 100px;" @click.native="testGetter">
{{JSON.stringify(activeComponent.properties)}}
</div> -->
</pane>
</template>
......@@ -32,8 +29,7 @@
import { mapState, mapActions, mapGetters } from 'vuex';
import Pane from '../../components/Pane';
import _ from 'lodash';
import properties from '../../utils/properties';
import { componentsMap } from '../../utils/common';
import { componentsMap, getCmpProps } from '../../utils/common';
import dynamicComponent from './components/dynamicComponent';
export default {
......@@ -41,30 +37,27 @@ export default {
components: { Pane, 'dynamic-component': dynamicComponent },
data() {
return {
componentsMap
componentsMap,
form: {
name: '',
type: '',
properties: {}
}
};
},
computed: {
...mapGetters(['activeComponent', 'activeComponentId']),
form: function() {
console.log(this.activeComponent.properties);
return this.activeComponent.properties;
},
...mapGetters(['activeComponent', 'componentList']),
cmpProps: function() {
if (!this.activeComponent.type) {
return [];
} else {
let _nodeProps = _.cloneDeep(properties.node); // Object.keys(properties.node) || [];
let _typeProps = {};
if (this.activeComponent.type !== 'node') {
_typeProps = _.cloneDeep(properties[this.activeComponent.type]); // Object.keys(properties[this.activeComponent.type]) || [];
}
return {
..._nodeProps,
..._typeProps
};
return getCmpProps(this.activeComponent.type);
}
},
watch: {
activeComponent: {
deep: true,
handler: function(val) {
this.form.name = val.name || '';
this.form.type = val.type || '';
this.form.properties = val.properties || {};
}
}
},
......@@ -72,7 +65,13 @@ export default {
testGetter() {
console.log(this.$store);
console.log('testGetter', this.activeComponentId, this.activeComponent);
console.log('testGetter', JSON.stringify(this.$store.state.project.data));
},
handleChange(label, v) {
this.$store.dispatch('modifyComponent', {
label: label,
value: v
});
}
}
};
......
<template>
<component :is="componentType" v-model="cmpValue" v-bind="itemProps" @change="v => handleChange(v)" @input="v=>handleInput(v)">
<d-slot
<component v-if="cmpValue !== null" :is="componentType" v-model="cmpValue" v-bind="itemProps" @change="v => handleChange(v)" @input="v=>handleInput(v)">
<d-slot
v-for="oitem in itemProps.options"
:is="itemProps.slotComponent"
:key="oitem.value"
......@@ -13,6 +13,7 @@
<script>
import swSlider from '../../../components/customSettings/slider';
import swSelect from '../../../components/customSettings/selector';
// 为什么上面template中的 :label要这么写?see :https://github.com/ElemeFE/element/issues/7910
// 判断的有点粗糙
......@@ -80,7 +81,7 @@ export default {
},
data() {
return {
cmpValue: ''
cmpValue: null
};
},
mounted() {
......@@ -88,43 +89,28 @@ export default {
if (_value === undefined || _value === null) {
_value = this.item.value;
}
// console.log('dynamic mounted', this.label, _value);
this.cmpValue = _value;
console.log('label value:', this.label, this.cmpValue);
},
watch: {
// properties: {
// deep: true,
// handler: function(value) {
// console.log('properties change', value);
// }
// }
},
computed: {
itemComponent() {
return componentMapper[this.item.type] || {};
},
// cmpValue() {
// if (!this.label) {
// return '';
// }
// let _value = this.properties[this.label];
// if (_value === undefined || _value === null) {
// _value = this.item.value;
// }
// return _value;
// },
itemProps() {
let _value = this.properties[this.label];
if (_value === undefined || _value === null) {
_value = this.item.value;
}
let result = {
size: 'mini',
...(this.itemComponent.props || {}),
...this.item.props,
options: this.item.options,
value: this.cmpValue
options: this.item.options || {}
// value: _value
};
if (this.item.title === '字体大小') {
console.log('itemProps', result);
}
// console.log('itemProps', this.componentType, this.label, result);
return result;
},
componentType() {
......@@ -138,18 +124,6 @@ export default {
label: this.label,
value: v
});
// const pathKeys = this.item.pathKeys;
// update fakeValue
// this.fakeValue = v;
// this.$store.dispatch(
// this.$store.getters.isBody() ? 'modifyPageSettings' : 'modifyComponent',
// {
// value: v,
// key: pathKeys.concat('v')
// }
// );
},
handleInput(v) {
if (this.item.type === 'input') {
......
......@@ -25,6 +25,7 @@
</template>
<script>
import { mapGetters } from 'vuex';
import { getComposedComponents } from '../../../utils/getComposedComponents';
import { styles } from '../../../utils/common';
import properties from '../../../utils/properties';
......@@ -136,8 +137,12 @@ export default {
}
},
computed: {
...mapGetters(['activeComponentId']),
active() {
return this.$store.state.project.activeIdList.indexOf(this.cmpId) !== -1;
// todo
console.log('active', this.componentData);
return this.activeComponentId === (this.componentData || {}).uuid;
},
isTyping() {
return this.componentData.editable;
......@@ -147,7 +152,9 @@ export default {
return this.componentData.group === 'text';
},
styleObject() {
return styles.getStyleObject(this.componentData.properties);
console.log('wrapper styleObject');
return styles.getComponentStyle(this.componentData);
},
position() {
const componentData = this.componentData;
......@@ -160,6 +167,15 @@ export default {
h: componentData.properties.height || _node.height.value
};
}
},
watch: {
componentData: {
deep: true,
handler: function(v) {
console.log('wrapper componentData change', v);
}
}
}
};
</script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment