Commit 74accbcfc538c91a083d7a34a0b492e4c3953b8b
Committed by
小小鲁班
1 parent
a81a9fb6
[feature] 解决更改组件border渲染问题
Showing
3 changed files
with
150 additions
and
32 deletions
front-end/h5/src/components/core/editor/right-panel/box-model/index.vue
| 1 | 1 | <template> |
| 2 | - <div class="box-model"> | |
| 2 | + <div v-if="editingElement" class="box-model"> | |
| 3 | 3 | <div v-if="lastSelect" class="prompt">设置 {{ lastSelect }} 大小</div> |
| 4 | 4 | <div v-else>选择 margin/border/padding 设置大小</div> |
| 5 | - <PositionCheckbox label="上" /> | |
| 5 | + <PositionCheckbox label="上" label-key="top" /> | |
| 6 | 6 | <div class="middle"> |
| 7 | - <PositionCheckbox label="左" /> | |
| 7 | + <PositionCheckbox label="左" label-key="left" /> | |
| 8 | 8 | <div ref="margin" class="margin" data-type="margin" @click="onBoxModelClick"> |
| 9 | 9 | margin |
| 10 | 10 | <div ref="border" class="border" data-type="border"> |
| ... | ... | @@ -14,13 +14,14 @@ |
| 14 | 14 | </div> |
| 15 | 15 | </div> |
| 16 | 16 | </div> |
| 17 | - <PositionCheckbox label="右" /> | |
| 17 | + <PositionCheckbox label="右" label-key="right" /> | |
| 18 | 18 | </div> |
| 19 | - <PositionCheckbox label="下" /> | |
| 19 | + <PositionCheckbox label="下" label-key="bottom" /> | |
| 20 | 20 | </div> |
| 21 | 21 | </template> |
| 22 | 22 | |
| 23 | 23 | <script> |
| 24 | + import { mapState, mapActions } from 'vuex' | |
| 24 | 25 | import PositionCheckbox from './position-checkbox' |
| 25 | 26 | export default { |
| 26 | 27 | name: 'BoxModel', |
| ... | ... | @@ -32,14 +33,24 @@ |
| 32 | 33 | lastSelect: '' |
| 33 | 34 | } |
| 34 | 35 | }, |
| 35 | - | |
| 36 | + computed: { | |
| 37 | + ...mapState('editor', { | |
| 38 | + editingElement: state => state.editingElement | |
| 39 | + }) | |
| 40 | + }, | |
| 36 | 41 | methods: { |
| 42 | + ...mapActions('editor', [ | |
| 43 | + 'setElementPosition' | |
| 44 | + ]), | |
| 37 | 45 | onBoxModelClick (e) { |
| 38 | 46 | const target = e.target |
| 39 | 47 | const classList = target.classList |
| 40 | 48 | const type = target.dataset.type |
| 41 | 49 | const selectClass = type + '-select' |
| 42 | - | |
| 50 | + // 更新选中的 boxModelPart,用于判断当前设置的是 margin / border / padding | |
| 51 | + this.setElementPosition({ | |
| 52 | + boxModelPart: type | |
| 53 | + }) | |
| 43 | 54 | if (this.lastSelect && type !== this.lastSelect) { |
| 44 | 55 | this.$refs[this.lastSelect].classList.remove(this.lastSelect + '-select') |
| 45 | 56 | } | ... | ... |
front-end/h5/src/components/core/editor/right-panel/box-model/position-checkbox.vue
| 1 | 1 | <template> |
| 2 | 2 | <div class="position-checkbox"> |
| 3 | - <div class="flex"> | |
| 4 | - <a-checkbox @change="onCheckboxChange"> | |
| 5 | - </a-checkbox> | |
| 6 | - <div class="label">{{label}}</div> | |
| 7 | - </div> | |
| 8 | - <a-input-number style="width:70px" v-model="value" :min="0" @change="onInputNumberChange" /> | |
| 9 | - <a-select default-value="px" style="width:70px"> | |
| 10 | - <a-select-option value="px"> | |
| 11 | - px | |
| 12 | - </a-select-option> | |
| 13 | - <a-select-option value="%"> | |
| 14 | - % | |
| 15 | - </a-select-option> | |
| 16 | - <a-select-option value="em"> | |
| 17 | - em | |
| 18 | - </a-select-option> | |
| 19 | - </a-select> | |
| 3 | + <!-- 只有选中 padding border margin 之后才会显示 --> | |
| 4 | + <template v-if="boxModelPart"> | |
| 5 | + <div class="flex"> | |
| 6 | + <a-checkbox @change="onCheckboxChange"> | |
| 7 | + </a-checkbox> | |
| 8 | + <div class="label">{{label}}</div> | |
| 9 | + </div> | |
| 10 | + <a-input-number style="width:70px" :value="value" :min="0" @change="onInputNumberChange" /> | |
| 11 | + <a-select :default-value="unitList[0]" style="width:70px"> | |
| 12 | + <a-select-option v-for="(item,index) in unitList" :key="index" :value="item"> | |
| 13 | + {{ item }} | |
| 14 | + </a-select-option> | |
| 15 | + </a-select> | |
| 16 | + </template> | |
| 20 | 17 | </div> |
| 21 | 18 | </template> |
| 22 | 19 | |
| 23 | 20 | <script> |
| 21 | + import { mapState, mapActions } from 'vuex' | |
| 24 | 22 | export default { |
| 25 | 23 | name: 'PositionCheckbox', |
| 26 | 24 | props: { |
| 27 | 25 | label: { |
| 28 | 26 | type: String, |
| 29 | 27 | default: '' |
| 28 | + }, | |
| 29 | + labelKey: { | |
| 30 | + type: String, | |
| 31 | + default: '' | |
| 30 | 32 | } |
| 31 | 33 | }, |
| 32 | - data () { | |
| 33 | - return { | |
| 34 | - value: 1 | |
| 34 | + computed: { | |
| 35 | + ...mapState('editor', { | |
| 36 | + editingElement: state => state.editingElement | |
| 37 | + }), | |
| 38 | + boxModelPart () { | |
| 39 | + return this.editingElement && this.editingElement.commonStyle.boxModelPart | |
| 40 | + }, | |
| 41 | + value () { | |
| 42 | + const { editingElement, labelKey, boxModelPart } = this | |
| 43 | + return this.boxModelPart ? editingElement.commonStyle[boxModelPart][labelKey].value : '' | |
| 44 | + }, | |
| 45 | + unitList () { | |
| 46 | + return this.boxModelPart === 'border' ? ['px', 'em'] : ['px', '%', 'em'] | |
| 35 | 47 | } |
| 36 | 48 | }, |
| 37 | - | |
| 38 | 49 | methods: { |
| 39 | - onCheckboxChange () {}, | |
| 40 | - onInputNumberChange () {} | |
| 50 | + ...mapActions('editor', [ | |
| 51 | + 'setElementPosition' | |
| 52 | + ]), | |
| 53 | + onCheckboxChange (e) { | |
| 54 | + }, | |
| 55 | + onInputNumberChange (value) { | |
| 56 | + const boxModelPart = this.boxModelPart | |
| 57 | + // 例如 boxModelPart 为 margin 时候 | |
| 58 | + const boxModelPartStyle = this.editingElement.commonStyle[boxModelPart] | |
| 59 | + // 更新值例如: padding-top | |
| 60 | + Object.assign(boxModelPartStyle[this.labelKey], { value }) | |
| 61 | + this.setElementPosition({ [boxModelPart]: boxModelPartStyle }) | |
| 62 | + } | |
| 41 | 63 | } |
| 42 | 64 | } |
| 43 | 65 | </script> | ... | ... |
front-end/h5/src/components/core/models/element.js
| ... | ... | @@ -13,7 +13,69 @@ const defaultStyle = { |
| 13 | 13 | textAlign: 'center', |
| 14 | 14 | color: '#000000', |
| 15 | 15 | backgroundColor: 'rgba(255, 255, 255, 0)', |
| 16 | - fontSize: 14 | |
| 16 | + fontSize: 14, | |
| 17 | + margin: { | |
| 18 | + top: { | |
| 19 | + value: 10, | |
| 20 | + unit: 'px' | |
| 21 | + }, | |
| 22 | + right: { | |
| 23 | + value: 0, | |
| 24 | + unit: 'px' | |
| 25 | + }, | |
| 26 | + bottom: { | |
| 27 | + value: 30, | |
| 28 | + unit: 'px' | |
| 29 | + }, | |
| 30 | + left: { | |
| 31 | + value: 0, | |
| 32 | + unit: 'px' | |
| 33 | + } | |
| 34 | + }, | |
| 35 | + padding: { | |
| 36 | + top: { | |
| 37 | + value: 0, | |
| 38 | + unit: 'px' | |
| 39 | + }, | |
| 40 | + right: { | |
| 41 | + value: 0, | |
| 42 | + unit: 'px' | |
| 43 | + }, | |
| 44 | + bottom: { | |
| 45 | + value: 0, | |
| 46 | + unit: 'px' | |
| 47 | + }, | |
| 48 | + left: { | |
| 49 | + value: 0, | |
| 50 | + unit: 'px' | |
| 51 | + } | |
| 52 | + }, | |
| 53 | + border: { | |
| 54 | + top: { | |
| 55 | + value: 0, | |
| 56 | + unit: 'px' | |
| 57 | + }, | |
| 58 | + right: { | |
| 59 | + value: 0, | |
| 60 | + unit: 'px' | |
| 61 | + }, | |
| 62 | + bottom: { | |
| 63 | + value: 0, | |
| 64 | + unit: 'px' | |
| 65 | + }, | |
| 66 | + left: { | |
| 67 | + value: 0, | |
| 68 | + unit: 'px' | |
| 69 | + }, | |
| 70 | + color: { | |
| 71 | + value: '#000' | |
| 72 | + }, | |
| 73 | + style: { | |
| 74 | + value: 'solid' | |
| 75 | + } | |
| 76 | + }, | |
| 77 | + 'border-style': 'solid', | |
| 78 | + boxModelPart: '' // 可选值 margin、padding、border | |
| 17 | 79 | } |
| 18 | 80 | |
| 19 | 81 | class Element { |
| ... | ... | @@ -75,7 +137,25 @@ class Element { |
| 75 | 137 | |
| 76 | 138 | return pluginProps |
| 77 | 139 | } |
| 78 | - | |
| 140 | + packPosData (obj, prefix) { | |
| 141 | + let init = {} | |
| 142 | + Object.keys(obj).forEach(key => { | |
| 143 | + init[prefix + '-' + key] = obj[key].value + (obj[key].unit || '') | |
| 144 | + }) | |
| 145 | + return init | |
| 146 | + } | |
| 147 | + packBorderData () { | |
| 148 | + const { top, right, bottom, left, color, style } = this.commonStyle.border | |
| 149 | + return { | |
| 150 | + /** | |
| 151 | + * 使用 border-left border-right 等方式,在 chrome 浏览器中会导致渲染问题 | |
| 152 | + * 这里就将他拼接成完整的 border-width解决bug,不知道是什么原因 | |
| 153 | + */ | |
| 154 | + 'border-width': `${top.value}px ${right.value}px ${bottom.value}px ${left.value}px `, | |
| 155 | + 'border-style': style.value, | |
| 156 | + 'border-color': color.value | |
| 157 | + } | |
| 158 | + } | |
| 79 | 159 | getStyle ({ position = 'static', isRem = false } = {}) { |
| 80 | 160 | if (this.name === 'lbp-background') { |
| 81 | 161 | return { |
| ... | ... | @@ -85,12 +165,17 @@ class Element { |
| 85 | 165 | } |
| 86 | 166 | const pluginProps = this.pluginProps |
| 87 | 167 | const commonStyle = this.commonStyle |
| 168 | + const { margin, padding } = commonStyle | |
| 169 | + // 由于在 defaultStyle 定义的时候是对象,这里需要将数据重新组装成 margin-top 这种形式 | |
| 170 | + const boxModel = { ...this.packPosData(margin, 'margin'), ...this.packPosData(padding, 'padding'), ...this.packBorderData() } | |
| 88 | 171 | let style = { |
| 89 | 172 | top: parsePx(pluginProps.top || commonStyle.top, isRem), |
| 90 | 173 | left: parsePx(pluginProps.left || commonStyle.left, isRem), |
| 91 | 174 | width: parsePx(pluginProps.width || commonStyle.width, isRem), |
| 92 | 175 | height: parsePx(pluginProps.height || commonStyle.height, isRem), |
| 93 | 176 | fontSize: parsePx(pluginProps.fontSize || commonStyle.fontSize, isRem), |
| 177 | + ...boxModel, | |
| 178 | + // 'border-style': commonStyle['border-style'], | |
| 94 | 179 | color: pluginProps.color || commonStyle.color, |
| 95 | 180 | // backgroundColor: pluginProps.backgroundColor || commonStyle.backgroundColor, |
| 96 | 181 | textAlign: pluginProps.textAlign || commonStyle.textAlign, | ... | ... |