Commit 5fe4ff5e authored by 郭志伟's avatar 郭志伟

feat: 分享和预览功能更新

parent 0509a199
<template>
<div class="form-list">
<Card class="form-list-card" :key="index" v-for="(item, index) in list">
<div slot='title' class="Fl-card-title">
<h4>项目{{index + 1}}</h4>
<a @click="del(index)">删除</a>
</div>
<Form @submit.native.prevent :model="item" :label-width="50">
<FormItem :prop="`${keywords.key}`" :label="keywords.name" :key="idx" v-for="(keywords, idx) in formControl">
<component :is="getComponent(keywords.type)" v-bind="keywords.props" :options="keywords.options" :disabled="shareDisableUrl(item, keywords)" v-model="item[keywords.key]" @input="handleModelChange($event, keywords.key, index)" />
</FormItem>
</Form>
</Card>
<div class="form-list-button">
<Button type="dashed" icon="plus-round" @click="add">添加项目</Button>
</div>
</div>
</template>
<script>
import FormList from '../mixins/formList.mixin.js';
import { SHOP_CART_CONFIG, SHARE_CONFIG, DEFAULT_CONFIG } from '@service/staticData.service';
const CONFIG_MAP = {
['购物车']: SHOP_CART_CONFIG,
['分享']: SHARE_CONFIG,
['自定义']: DEFAULT_CONFIG
};
export default {
mixins: [FormList],
data() {
return {
oldPersets: []
};
},
methods: {
shareDisableUrl(item, keywords) {
return item.persets === '分享' && keywords.key === 'url';
},
handleModelChange(e, key, index) {
if (key !== 'persets') return;
if (e !== this.oldPersets[index]) {
this.$set(this.list, index, JSON.parse(JSON.stringify(CONFIG_MAP[e])));
this.oldPersets[index] = e;
}
}
}
}
</script>
<style lang="less" scoped>
.form-list {
/deep/ .ivu-card {
margin-bottom: 16px;
.ivu-form {
.ivu-form-item {
display: flex;
margin-bottom: 12px;
}
.ivu-form-item-label {
text-align: left;
width: 60px !important;
font-size: 12px;
}
.ivu-form-item-content {
flex: 1 !important;
}
}
&:hover {
box-shadow: 0 0 14px 0 rgba(46,140,240,0.20);
border: 1px solid #2E8CF0 !important;
}
}
h3 {
padding-bottom: 10px;
text-align: center;
}
&-card {
/deep/ .ivu-card-head{
background: #f0f2f5;
}
/deep/ .ivu-card-body {
padding: 10px !important;
}
.Fl-card-title {
display: flex;
justify-content: space-between;
}
}
&-button {
// margin-top: 20px;
// text-align: center;
}
}
</style>
\ No newline at end of file
......@@ -9,7 +9,7 @@
<Icon type="ios-arrow-forward" />
</div>
<div class="color-selector" v-if="showToColor">
<Input class="color-selector-input" v-model="toColor" placeholder="请输入" @input="change"></Input>
<Input class="color-selector-input" v-model="toColor" placeholder="请输入" @input="change($event, 'to')"></Input>
<ColorPicker placement="bottom-end" v-model="toColor" :alpha="alpha" @on-active-change="change($event, 'to')" @on-change="change($event, 'to')" />
</div>
</div>
......@@ -53,13 +53,14 @@
if (val && this.toColor) {
this.$emit('input', [this.color, this.toColor]);
} else {
this.toColor = '';
this.$emit('input', this.color);
}
}
},
methods: {
change(val, type) {
if (type === 'to') {
if (type === 'to' || this.toColor) {
this.$emit('input', [this.color, this.toColor]);
} else {
this.$emit('input', val);
......
......@@ -7,7 +7,7 @@
</div>
<Form @submit.native.prevent :model="item" :label-width="50">
<FormItem :prop="`${keywords.key}`" :label="keywords.name" :key="idx" v-for="(keywords, idx) in formControl">
<component :is="getComponent(keywords.type)" :options="item.options" v-model="item[keywords.key]" />
<component :is="getComponent(keywords.type)" v-bind="keywords.props" :options="keywords.options" v-model="item[keywords.key]" />
</FormItem>
</Form>
</Card>
......@@ -17,72 +17,9 @@
</div>
</template>
<script>
import ComponentSelect from '../ComponentSelect/index.vue';
import Upload from '../Upload/index.vue';
import ColorSelector from '../ColorSelector/index.vue';
import FormList from '../mixins/formList.mixin.js';
export default {
components: {
ComponentSelect,
Upload,
ColorSelector
},
props: {
value: {
type: Array,
default: () => []
},
formControl: {
type: Array,
default: () => []
},
name: String
},
data() {
return {
list: this.value,
}
},
watch: {
list(val) {
this.$emit('input', val);
},
value(val) {
this.list = val;
}
},
methods: {
getComponent(type) {
let result = type;
switch (type) {
case 'text':
result = 'Input';
break;
case 'select':
result = 'BaseSelect';
break;
case 'checkbox' :
result = 'Checkbox';
break;
case 'textarea' :
result = 'Textarea';
break;
case 'number' :
result = 'Number';
break;
}
return result;
},
add() {
const object = this.formControl.reduce((pre, cur) => {
pre[cur.key] = '';
return pre;
}, {})
this.list.push(object);
},
del(index) {
this.list.splice(index, 1);
}
}
mixins: [FormList]
}
</script>
<style lang="less" scoped>
......
<template>
<iSwitch v-model="switchVal" size="large" class="switch-btn">
<span slot="open">开启</span>
<span slot="close">关闭</span>
</iSwitch>
</template>
<script>
export default {
props: {
value: Boolean,
},
data() {
return {
switchVal: this.value,
}
},
watch: {
switchVal(val) {
this.$emit('input', val);
}
}
}
</script>
<style scoped>
.switch-btn {
transform: scale(0.8);
margin-left: -7px;
}
</style>
\ No newline at end of file
......@@ -8,9 +8,11 @@ import BaseSelect from '../DynamicForm/component/BaseSelect/index.vue';
import Textarea from '../DynamicForm/component/Textarea/index.vue';
import Number from '../DynamicForm/component/Number/index.vue';
import FormList from '../DynamicForm/component/FormList/index.vue';
import { resizeDiv, getStyle } from '@/service/utils.service';
import BackTopPicker from '../DynamicForm/component/BackTopPicker/index.vue';
import SwitchBtn from '../DynamicForm/component/SwitchBtn/index.vue';
import { SHOP_CART_CONFIG } from '@service/staticData.service';
@Component({ components: { Upload, ColorSelector, BaseSelect, Textarea, Number, FormList }, name: 'DynamicPageForm' })
@Component({ components: { Upload, ColorSelector, BaseSelect, Textarea, Number, FormList, BackTopPicker, SwitchBtn }, name: 'DynamicPageForm' })
export default class DynamicPageForm extends Mixins(ContextMenuMixin) {
@Getter('pageData') pageData;
......@@ -29,21 +31,22 @@ export default class DynamicPageForm extends Mixins(ContextMenuMixin) {
type: 'ColorSelector'
}
];
propsSchame: object[] = [
titleSchema: object[] = [
{
key: 'titleBgColor',
name: '标题栏背景色',
name: '背景色',
type: 'ColorSelector',
props: {
gradient: true,
alpha: false
}
},
];
bottomSchema: object[] = [
{
key: 'showPageBottomTip',
name: '底部提示',
type: 'checkbox'
name: '状态',
type: 'SwitchBtn'
},
{
key: 'pageBottomTxt',
......@@ -55,26 +58,32 @@ export default class DynamicPageForm extends Mixins(ContextMenuMixin) {
name: '提示颜色',
type: 'ColorSelector'
},
];
floatSchema: object[] = [
{
key: 'showBackTop',
name: '返回顶部',
type: 'checkbox'
type: 'SwitchBtn'
},
{
key: 'btAttachVal',
name: '添加按钮',
desc: '添加按钮',
type: 'FormList',
type: 'BackTopPicker',
formControl: [
{
key: 'name',
name: '名称',
type: 'text'
key: 'persets',
name: '预设属性',
type: 'select',
options: ['购物车', '分享', '自定义']
},
{
key: 'txt',
name: '内容',
type: 'text'
name: '文字',
type: 'text',
props: {
placeholder: '(选填) 图标下方文字',
}
},
{
key: 'url',
......@@ -91,6 +100,11 @@ export default class DynamicPageForm extends Mixins(ContextMenuMixin) {
name: '文字颜色',
type: 'ColorSelector'
},
{
key: 'iconColor',
name: '图标颜色',
type: 'ColorSelector'
},
{
key: 'background',
name: '背景颜色',
......@@ -99,7 +113,20 @@ export default class DynamicPageForm extends Mixins(ContextMenuMixin) {
{
key: 'size',
name: '尺寸',
type: 'Slider'
type: 'Slider',
props: {
min: 14,
max: 68
}
},
{
key: 'iconSize',
name: '图标尺寸',
type: 'Slider',
props: {
min: 14,
max: 68
}
},
{
key: 'radius',
......@@ -111,16 +138,19 @@ export default class DynamicPageForm extends Mixins(ContextMenuMixin) {
name: '阴影',
type: 'checkbox'
}
]
},
],
formDefault: SHOP_CART_CONFIG
}
];
get propsSchema() {
return [...this.titleSchema, ...this.bottomSchema, ...this.floatSchema];
}
@Watch('pageData', { immediate: true, deep: true })
onElementChange(newVal) {
onElementChange() {
this.commonStyleSchame.forEach(schame => {
this.$set(this.commonStyleForm, schame.key, this.pageData?.commonStyle[schame.key]);
});
this.propsSchame.forEach(schame => {
this.propsSchema.forEach(schame => {
this.$set(this.propsForm, schame.key, this.pageData?.props?.[schame.key]);
});
}
......
<template>
<div class="dynamic-form">
<h2>{{title}}</h2>
<Form class="dynamic-form-component" :label-width="80" :model="propsForm" @submit.native.prevent>
<h3>基础属性</h3>
<template v-for="(item, index) in propsSchame">
<FormItem class="Df-component-formitem" :label="item.name" >
<Form class="dynamic-form-component" :label-width="80" :model="propsForm" @submit.native.prevent>
<h3>基础属性</h3>
<h4>标题栏</h4>
<template v-for="(item, index) in titleSchema">
<FormItem class="Df-component-formitem" :label="item.name" :key="'titleSchema_' + index">
<component :is="item.type" :options="item.options" v-bind="item.props" v-model="propsForm[item.key]" :formControl="item.formControl" />
</FormItem>
</template>
<h4>底部提示</h4>
<template v-for="(item, index) in bottomSchema">
<FormItem class="Df-component-formitem" :label="item.name" :key="'bottomSchema_' + index">
<component :is="item.type" :options="item.options" v-bind="item.props" v-model="propsForm[item.key]" :formControl="item.formControl" />
</FormItem>
</template>
</Form>
<Form class="dynamic-form-component" :label-width="80" :model="commonStyleForm" @submit.native.prevent>
<h3>基础样式</h3>
<template v-for="(item, index) in commonStyleSchame">
<FormItem class="Df-component-formitem" :label="item.name" >
<component :is="item.type" :options="item.options" v-bind="item.props" v-model="commonStyleForm[item.key]" />
<h4>悬浮窗</h4>
<template v-for="(item, index) in floatSchema">
<FormItem class="Df-component-formitem" :label="item.name" :key="'floatSchema_' + index">
<component :is="item.type" :options="item.options" v-bind="item.props" v-model="propsForm[item.key]" :formDefault="item.formDefault" :formControl="item.formControl" />
</FormItem>
</template>
</Form>
<Form class="dynamic-form-component" :label-width="80" :model="commonStyleForm" @submit.native.prevent>
<h3>基础样式</h3>
<template v-for="(item, index) in commonStyleSchame">
<FormItem class="Df-component-formitem" :label="item.name" :key="index">
<component :is="item.type" :options="item.options" v-bind="item.props" v-model="commonStyleForm[item.key]" />
</FormItem>
</template>
</Form>
</div>
</template>
......@@ -28,7 +41,7 @@
border-bottom: 8px solid #F5F6FA;
text-align: center;
}
h3 {
h3, h4 {
padding: 10px 0;
margin-bottom: 10px;
border-bottom: 1px solid #ebeef5;
......
<template>
<cr-back-top :show-back-top="false" :list="list" class="back-top-preview" />
</template>
<script>
import { BACK_TOP_CONFIG } from '@service/staticData.service';
import { mapGetters } from 'vuex';
export default {
name: 'BackTopPreview',
data() {
return {
oldPersets: []
};
},
computed: {
...mapGetters(['pageData']),
list() {
const { btAttachVal, showBackTop } = this.pageData.props;
const btnGroup = btAttachVal.map(item => {
let it = JSON.parse(JSON.stringify(item));
delete it.url;
return it;
});
if (showBackTop) {
btnGroup.splice(0, 0, BACK_TOP_CONFIG);
}
return btnGroup;
}
},
}
</script>
<style lang="less" scoped>
.back-top-preview {
position: absolute;
}
</style>
\ No newline at end of file
<template>
<cr-nav-bar
class="nav-bar"
:title="tille"
:left-arrow="false"
left-text=""
:fixed="false"
:style="titleStyle"
/>
</template>
<script>
import { hexToRgb } from '@service/color.service';
import { mapGetters } from 'vuex';
export default {
name: 'TitlePreview',
computed: {
...mapGetters(['pageData']),
tille() {
return '标题';
},
titleStyle() {
const { titleBgColor } = this.pageData.props;
return this.getTitleStyle(titleBgColor);
}
},
methods: {
getTitleStyle(bgcolor = "#fff") {
if (!EASY_ENV_IS_BROWSER) return;
const DARK_COLOR = "#000";
const LIGHT_COLOR = "#fff";
const isGradient = Array.isArray(bgcolor);
let rgbColor = isGradient ? bgcolor[0] : bgcolor;
if (rgbColor.toLocaleLowerCase().indexOf("rgb") === -1) rgbColor = hexToRgb(rgbColor);
const isDarkContent = 0.213 * rgbColor[0] + 0.715 * rgbColor[1] + 0.072 * rgbColor[2] <= 255 / 2;
const color = isDarkContent ? LIGHT_COLOR : DARK_COLOR;
const background = isGradient ? `linear-gradient(90deg, ${bgcolor[0]}, ${bgcolor[1]})` : bgcolor;
return {
color,
background
};
}
}
}
</script>
<style lang="less" scoped>
@deep: ~'>>>';
.nav-bar {
@{deep} .cr-nav-bar {
z-index: inherit !important;
background-color: transparent;
&__title {
color: inherit;
}
}
}
</style>
......@@ -4,8 +4,9 @@ import type { PageInfo, Page, GridLayout } from '@store/modules/editor/state';
import TransformStyleMixin from '@page/mixins/transformStyle.mixin';
import FreedomContainer from '@editor/component/FreedomContainer/index.vue';
import PageBottomTip from './component/PageBottomTip/index.vue';
@Component({ components: { FreedomContainer, PageBottomTip }, name: 'OperationPanel' })
import BackTopPreview from './component/BackTopPreview/index.vue';
import TitlePreview from './component/TitlePreview/index.vue';
@Component({ components: { FreedomContainer, PageBottomTip, BackTopPreview, TitlePreview }, name: 'OperationPanel' })
export default class OperationPanel extends Mixins(TransformStyleMixin) {
@Getter('pageData') pageData;
@State(state => state.editor.gridLayout) gridLayout?: GridLayout;
......
<template>
<div :class="[{'Dcm-container-panel_in': isDragIn, 'Dcm-container-panel_draging': isDraging}, 'Dcm-container-panel']" @dragover.prevent @dragenter="dragenter" @dragover="dragover"
@dragleave="dragleave" @drop="drops">
<grid-layout
:layout.sync="layout"
:col-num="gridLayout.colNum"
:row-height="gridLayout.rowHeight"
:margin="[0, 0]"
:is-draggable="gridLayout.draggable"
:is-resizable="true"
:is-mirrored="false"
:vertical-compact="true"
:use-css-transforms="true"
:style="transformStyle(pageData.commonStyle)"
@click.native.stop="toggle(false)"
@layout-updated="layoutUpdatedEvent"
>
<grid-item @click.native.stop="handleElementClick(index, null)" v-for="(item, index) in pageData.elements"
:x="item.point.x"
:y="item.point.y"
:w="item.point.w"
:h="item.point.h"
:i="item.point.i"
:key="item.point.i + index"
@contextmenu.native.prevent="show($event, index)"
@resized="resizedEvent"
@moved="movedEvent"
:style="transformStyle(item.commonStyle, item.name)"
:class="{'Dcmcp-item_selected': curEleIndex === index && curChildIndex === null}">
<component ref="container" :id="item.id" class="Dcmcp-item-com" @handleElementClick="handleElementClick" :containerIndex="index" :childItem="item" :is="item.name" :key="index" v-bind="item.props"></component>
</grid-item>
<grid-item
v-if="pageData.props.showPageBottomTip"
:x="bottomInfo.x"
:y="bottomInfo.y"
:w="bottomInfo.w"
:h="bottomInfo.h"
:i="bottomInfo.i"
:static="true"
:key="bottomInfo.i + pageData.elements.length"
<div class="Dcm-container-panel_container">
<title-preview />
<div :class="[{'Dcm-container-panel_in': isDragIn, 'Dcm-container-panel_draging': isDraging}, 'Dcm-container-panel']" @dragover.prevent @dragenter="dragenter" @dragover="dragover"
@dragleave="dragleave" @drop="drops">
<grid-layout
:layout.sync="layout"
:col-num="gridLayout.colNum"
:row-height="gridLayout.rowHeight"
:margin="[0, 0]"
:is-draggable="gridLayout.draggable"
:is-resizable="true"
:is-mirrored="false"
:vertical-compact="true"
:use-css-transforms="true"
:style="transformStyle(pageData.commonStyle)"
@click.native.stop="toggle(false)"
@layout-updated="layoutUpdatedEvent"
>
<page-bottom-tip />
</grid-item>
</grid-layout>
<grid-item @click.native.stop="handleElementClick(index, null)" v-for="(item, index) in pageData.elements"
:x="item.point.x"
:y="item.point.y"
:w="item.point.w"
:h="item.point.h"
:i="item.point.i"
:key="item.point.i + index"
@contextmenu.native.prevent="show($event, index)"
@resized="resizedEvent"
@moved="movedEvent"
:style="transformStyle(item.commonStyle, item.name)"
:class="{'Dcmcp-item_selected': curEleIndex === index && curChildIndex === null}">
<component ref="container" :id="item.id" class="Dcmcp-item-com" @handleElementClick="handleElementClick" :containerIndex="index" :childItem="item" :is="item.name" :key="index" v-bind="item.props"></component>
</grid-item>
<grid-item
v-if="pageData.props.showPageBottomTip"
:x="bottomInfo.x"
:y="bottomInfo.y"
:w="bottomInfo.w"
:h="bottomInfo.h"
:i="bottomInfo.i"
:static="true"
:key="bottomInfo.i + pageData.elements.length"
>
<page-bottom-tip />
</grid-item>
</grid-layout>
</div>
<back-top-preview />
</div>
</template>
<script lang="ts" src="./index.ts"></script>
......@@ -60,16 +64,21 @@
}
.Dcm-container-panel {
margin: 30px auto;
&_container {
position: relative;
width: 375px;
height: 667px;
margin: 30px auto;
box-shadow: 2px 3px 10px rgba(0, 0, 0, 0.2);
}
width: 375px;
height: 667px;
min-height: 667px;
height: 619px;
min-height: 619px;
overflow-y: scroll;
background-color: rgb(244, 244, 244);
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.2);
/deep/ .vue-grid-layout {
min-height: 667px;
min-height: 619px;
.vue-grid-item {
display: flex;
......
......@@ -15,7 +15,6 @@ import localStorage from '@service/localStorage.service';
import EventBus from '@service/eventBus.service';
import { getStyle } from '@service/utils.service';
import OperationPanel from '@editor/component/OperationPanel/index.vue';
import type { PageInfo, Page, GridLayout } from '../../../store/modules/editor/state';
@Component({components: { GridLayout: VueGridLayout.GridLayout,
......
......@@ -89,11 +89,16 @@ export const defaultState = {
showBackTop: true,
btAttachVal: [
{
name: '购物车',
persets: '购物车',
icon: 'shopping-cart-o',
url: 'xyqb://shoppingCartTab',
color: '#333',
background: '#fff'
iconColor: '#333',
background: '#fff',
shadow: true,
radius: true,
size: 46,
iconSize: 20
}
],
},
......
// tslint:disable
import { isApp } from './utils.service';
// RGB转HEX
export function rgbToHex(r, g, b) {
const hex = ((r << 16) | (g << 8) | b).toString(16);
return "#" + new Array(Math.abs(hex.length - 7)).join("0") + hex;
}
// HEX转RGB
export function hexToRgb(hex) {
var rgb = [];
for (let i = 1; i < 7; i += 2) {
rgb.push(parseInt("0x" + hex.slice(i, i + 2)));
}
return rgb;
}
// 计算RGB渐变色色值
export function gradient(startColor, endColor, step) {
// 将 hex 转换为rgb
let sColor = hexToRgb(startColor);
let eColor = hexToRgb(endColor);
// 计算R\G\B每一步的差值
let rStep = (eColor[0] - sColor[0]) / step;
let gStep = (eColor[1] - sColor[1]) / step;
let bStep = (eColor[2] - sColor[2]) / step;
let gradientColorArr = [];
for (let i = 0; i < step; i++) {
// 计算每一步的hex值
gradientColorArr.push(
rgbToHex(
parseInt(rStep * i + sColor[0]),
parseInt(gStep * i + sColor[1]),
parseInt(bStep * i + sColor[2])
)
);
}
return gradientColorArr;
}
export function colorToRgb(color) {
// 16进制颜色值的正则
let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
// 把颜色值变成小写
color = color.toLowerCase();
if (reg.test(color)) {
// 如果只有三位的值,需变成六位,如:#fff => #ffffff
if (color.length === 4) {
let colorNew = "#";
for (let i = 1; i < 4; i += 1) {
colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1));
}
color = colorNew;
}
// 处理六位的颜色值,转为RGB
let colorChange = [];
for (let i = 1; i < 7; i += 2) {
colorChange.push(parseInt("0x" + color.slice(i, i + 2)));
}
return "RGB(" + colorChange.join(",") + ")";
} else {
return color;
}
}
// RGB TO RGBA
export function rgbToRgba(rgb, opacity) {
return rgb.replace(")", `, ${opacity})`);
}
// 判断色值
export function isColor(color) {
var re1 = /^#([0-9a-f]{6}|[0-9a-f]{3})$/i;
var re2 = /^rgb\(([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\\,([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\\,([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\)$/i;
var re3 = /^rgba\(([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\\,([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\\,([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\\,(1|1.0|0.[0-9])\)$/i;
return re2.test(color) || re1.test(color) || re3.test(color);
}
'use strict';
export const SHOP_CART_CONFIG = {
persets: '购物车',
txt: '',
icon: 'shopping-cart-o',
url: 'xyqb://shoppingCartTab',
color: '#333',
iconColor: '#333',
background: '#fff',
shadow: true,
radius: true,
size: 46,
iconSize: 20
};
export const BACK_TOP_CONFIG = {
persets: '自定义',
icon: 'back-top-o',
background: '#fff',
color: '#333',
size: 46,
iconSize: 20,
txt: '顶部'
};
export const SHARE_CONFIG = {
persets: '分享',
txt: '',
icon: 'share',
url: '',
color: '#333',
iconColor: '#333',
background: '#fff',
shadow: true,
radius: true,
size: 46,
iconSize: 20
};
export const DEFAULT_CONFIG = {
persets: '自定义',
txt: '',
icon: '',
url: '',
color: '#333',
iconColor: '#333',
background: '#fff',
shadow: true,
radius: true,
size: 46,
iconSize: 20
};
\ No newline at end of file
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