mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
feat: 添加形状翻转
This commit is contained in:
parent
5910c3fce3
commit
2838426ca9
@ -70,6 +70,7 @@ npm run serve
|
|||||||
- 边框
|
- 边框
|
||||||
- 阴影
|
- 阴影
|
||||||
- 透明度
|
- 透明度
|
||||||
|
- 翻转
|
||||||
### 线条
|
### 线条
|
||||||
- 颜色
|
- 颜色
|
||||||
- 宽度
|
- 宽度
|
||||||
|
@ -41,6 +41,10 @@ export interface PPTTextElement {
|
|||||||
shadow?: PPTElementShadow;
|
shadow?: PPTElementShadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ImageOrShapeFlip {
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
}
|
||||||
export interface ImageElementFilters {
|
export interface ImageElementFilters {
|
||||||
'blur'?: string;
|
'blur'?: string;
|
||||||
'brightness'?: string;
|
'brightness'?: string;
|
||||||
@ -68,7 +72,7 @@ export interface PPTImageElement {
|
|||||||
range: [[number, number], [number, number]];
|
range: [[number, number], [number, number]];
|
||||||
shape: 'rect' | 'roundRect' | 'ellipse' | 'triangle' | 'pentagon' | 'rhombus' | 'star';
|
shape: 'rect' | 'roundRect' | 'ellipse' | 'triangle' | 'pentagon' | 'rhombus' | 'star';
|
||||||
};
|
};
|
||||||
flip?: { x?: number; y?: number };
|
flip?: ImageOrShapeFlip;
|
||||||
shadow?: PPTElementShadow;
|
shadow?: PPTElementShadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +98,7 @@ export interface PPTShapeElement {
|
|||||||
rotate?: number;
|
rotate?: number;
|
||||||
outline?: PPTElementOutline;
|
outline?: PPTElementOutline;
|
||||||
opacity?: number;
|
opacity?: number;
|
||||||
|
flip?: ImageOrShapeFlip;
|
||||||
shadow?: PPTElementShadow;
|
shadow?: PPTElementShadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,19 +59,7 @@
|
|||||||
<Button class="full-width-btn"><IconColorFilter class="btn-icon" /> 设置滤镜</Button>
|
<Button class="full-width-btn"><IconColorFilter class="btn-icon" /> 设置滤镜</Button>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
<CheckboxButtonGroup class="row">
|
<ElementFlip />
|
||||||
<CheckboxButton
|
|
||||||
style="flex: 1;"
|
|
||||||
:checked="flip.x === 180"
|
|
||||||
@click="updateImage({ flip: { x: flip.x === 180 ? 0 : 180, y: flip.y } })"
|
|
||||||
><IconFlipVertically /> 水平翻转</CheckboxButton>
|
|
||||||
<CheckboxButton
|
|
||||||
style="flex: 1;"
|
|
||||||
:checked="flip.y === 180"
|
|
||||||
@click="updateImage({ flip: { x: flip.x, y: flip.y === 180 ? 0 : 180 } })"
|
|
||||||
><IconFlipHorizontally /> 垂直翻转</CheckboxButton>
|
|
||||||
</CheckboxButtonGroup>
|
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<ElementOutline />
|
<ElementOutline />
|
||||||
<Divider />
|
<Divider />
|
||||||
@ -96,6 +84,7 @@ import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
|||||||
|
|
||||||
import ElementOutline from '../common/ElementOutline.vue'
|
import ElementOutline from '../common/ElementOutline.vue'
|
||||||
import ElementShadow from '../common/ElementShadow.vue'
|
import ElementShadow from '../common/ElementShadow.vue'
|
||||||
|
import ElementFlip from '../common/ElementFlip.vue'
|
||||||
|
|
||||||
interface FilterOption {
|
interface FilterOption {
|
||||||
label: string;
|
label: string;
|
||||||
@ -156,6 +145,7 @@ export default defineComponent({
|
|||||||
components: {
|
components: {
|
||||||
ElementOutline,
|
ElementOutline,
|
||||||
ElementShadow,
|
ElementShadow,
|
||||||
|
ElementFlip,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
@ -164,24 +154,11 @@ export default defineComponent({
|
|||||||
|
|
||||||
const clipPanelVisible = ref(false)
|
const clipPanelVisible = ref(false)
|
||||||
|
|
||||||
const flip = ref({
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
const filterOptions = ref<FilterOption[]>(JSON.parse(JSON.stringify(defaultFilters)))
|
const filterOptions = ref<FilterOption[]>(JSON.parse(JSON.stringify(defaultFilters)))
|
||||||
|
|
||||||
watch(handleElement, () => {
|
watch(handleElement, () => {
|
||||||
if (!handleElement.value || handleElement.value.type !== 'image') return
|
if (!handleElement.value || handleElement.value.type !== 'image') return
|
||||||
|
|
||||||
if (handleElement.value.flip) {
|
|
||||||
flip.value = {
|
|
||||||
x: handleElement.value.flip.x || 0,
|
|
||||||
y: handleElement.value.flip.y || 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else flip.value = { x: 0, y: 0 }
|
|
||||||
|
|
||||||
const filters = handleElement.value.filters
|
const filters = handleElement.value.filters
|
||||||
if (filters) {
|
if (filters) {
|
||||||
filterOptions.value = defaultFilters.map(item => {
|
filterOptions.value = defaultFilters.map(item => {
|
||||||
@ -194,11 +171,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
const { addHistorySnapshot } = useHistorySnapshot()
|
const { addHistorySnapshot } = useHistorySnapshot()
|
||||||
|
|
||||||
const updateImage = (props: Partial<PPTImageElement>) => {
|
|
||||||
store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
|
|
||||||
addHistorySnapshot()
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateFilter = (filter: FilterOption, value: number) => {
|
const updateFilter = (filter: FilterOption, value: number) => {
|
||||||
const originFilters = handleElement.value.filters || {}
|
const originFilters = handleElement.value.filters || {}
|
||||||
const filters = { ...originFilters, [filter.key]: `${value}${filter.unit}` }
|
const filters = { ...originFilters, [filter.key]: `${value}${filter.unit}` }
|
||||||
@ -337,9 +309,7 @@ export default defineComponent({
|
|||||||
shapeClipPathOptions,
|
shapeClipPathOptions,
|
||||||
ratioClipOptions,
|
ratioClipOptions,
|
||||||
filterOptions,
|
filterOptions,
|
||||||
flip,
|
|
||||||
handleElement,
|
handleElement,
|
||||||
updateImage,
|
|
||||||
updateFilter,
|
updateFilter,
|
||||||
clipImage,
|
clipImage,
|
||||||
presetImageClip,
|
presetImageClip,
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<ElementFlip />
|
||||||
<Divider />
|
<Divider />
|
||||||
<ElementOutline />
|
<ElementOutline />
|
||||||
<Divider />
|
<Divider />
|
||||||
@ -86,6 +87,7 @@ import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
|||||||
import ElementOpacity from '../common/ElementOpacity.vue'
|
import ElementOpacity from '../common/ElementOpacity.vue'
|
||||||
import ElementOutline from '../common/ElementOutline.vue'
|
import ElementOutline from '../common/ElementOutline.vue'
|
||||||
import ElementShadow from '../common/ElementShadow.vue'
|
import ElementShadow from '../common/ElementShadow.vue'
|
||||||
|
import ElementFlip from '../common/ElementFlip.vue'
|
||||||
import ColorButton from '../common/ColorButton.vue'
|
import ColorButton from '../common/ColorButton.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -94,6 +96,7 @@ export default defineComponent({
|
|||||||
ElementOpacity,
|
ElementOpacity,
|
||||||
ElementOutline,
|
ElementOutline,
|
||||||
ElementShadow,
|
ElementShadow,
|
||||||
|
ElementFlip,
|
||||||
ColorButton,
|
ColorButton,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
|
70
src/views/Editor/Toolbar/common/ElementFlip.vue
Normal file
70
src/views/Editor/Toolbar/common/ElementFlip.vue
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<div class="element-flip">
|
||||||
|
<CheckboxButtonGroup class="row">
|
||||||
|
<CheckboxButton
|
||||||
|
style="flex: 1;"
|
||||||
|
:checked="flip.x === 180"
|
||||||
|
@click="updateFlip({ x: flip.x === 180 ? 0 : 180, y: flip.y })"
|
||||||
|
><IconFlipVertically /> 水平翻转</CheckboxButton>
|
||||||
|
<CheckboxButton
|
||||||
|
style="flex: 1;"
|
||||||
|
:checked="flip.y === 180"
|
||||||
|
@click="updateFlip({ x: flip.x, y: flip.y === 180 ? 0 : 180 })"
|
||||||
|
><IconFlipHorizontally /> 垂直翻转</CheckboxButton>
|
||||||
|
</CheckboxButtonGroup>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, ref, watch } from 'vue'
|
||||||
|
import { MutationTypes, useStore } from '@/store'
|
||||||
|
import { PPTImageElement, PPTShapeElement } from '@/types/slides'
|
||||||
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'element-flip',
|
||||||
|
setup() {
|
||||||
|
const store = useStore()
|
||||||
|
const handleElement = computed<PPTImageElement | PPTShapeElement>(() => store.getters.handleElement)
|
||||||
|
|
||||||
|
const flip = ref({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(handleElement, () => {
|
||||||
|
if (!handleElement.value || !['image', 'shape'].includes(handleElement.value.type)) return
|
||||||
|
|
||||||
|
if (handleElement.value.flip) {
|
||||||
|
flip.value = {
|
||||||
|
x: handleElement.value.flip.x || 0,
|
||||||
|
y: handleElement.value.flip.y || 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else flip.value = { x: 0, y: 0 }
|
||||||
|
}, { deep: true, immediate: true })
|
||||||
|
|
||||||
|
const { addHistorySnapshot } = useHistorySnapshot()
|
||||||
|
|
||||||
|
const updateFlip = (value: number) => {
|
||||||
|
const props = { flip: value }
|
||||||
|
store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
|
||||||
|
addHistorySnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
flip,
|
||||||
|
updateFlip,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.row {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -13,7 +13,7 @@
|
|||||||
class="element-content"
|
class="element-content"
|
||||||
:style="{
|
:style="{
|
||||||
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||||
transform: flip,
|
transform: flipStyle,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ImageRectOutline
|
<ImageRectOutline
|
||||||
@ -66,6 +66,7 @@ import ImageEllipseOutline from './ImageEllipseOutline.vue'
|
|||||||
import ImagePolygonOutline from './ImagePolygonOutline.vue'
|
import ImagePolygonOutline from './ImagePolygonOutline.vue'
|
||||||
|
|
||||||
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||||
|
import useElementFlip from '@/views/components/element/hooks/useElementFlip'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'base-element-image',
|
name: 'base-element-image',
|
||||||
@ -122,23 +123,17 @@ export default defineComponent({
|
|||||||
return filter
|
return filter
|
||||||
})
|
})
|
||||||
|
|
||||||
const flip = computed(() => {
|
|
||||||
if (!props.elementInfo.flip) return ''
|
|
||||||
const { x, y } = props.elementInfo.flip
|
|
||||||
if (x && y) return `rotateX(${x}deg) rotateY(${y}deg)`
|
|
||||||
else if (x) return `rotateX(${x}deg)`
|
|
||||||
else if (y) return `rotateY(${y}deg)`
|
|
||||||
return ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const shadow = computed(() => props.elementInfo.shadow)
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
const { shadowStyle } = useElementShadow(shadow)
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
|
|
||||||
|
const flip = computed(() => props.elementInfo.flip)
|
||||||
|
const { flipStyle } = useElementFlip(flip)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
imgPosition,
|
imgPosition,
|
||||||
clipShape,
|
clipShape,
|
||||||
filter,
|
filter,
|
||||||
flip,
|
flipStyle,
|
||||||
shadowStyle,
|
shadowStyle,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
v-contextmenu="contextmenus"
|
v-contextmenu="contextmenus"
|
||||||
:style="{
|
:style="{
|
||||||
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||||
transform: flip,
|
transform: flipStyle,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ImageRectOutline
|
<ImageRectOutline
|
||||||
@ -77,6 +77,7 @@ import { PPTImageElement } from '@/types/slides'
|
|||||||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||||
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
||||||
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||||
|
import useElementFlip from '@/views/components/element/hooks/useElementFlip'
|
||||||
|
|
||||||
import ImageRectOutline from './ImageRectOutline.vue'
|
import ImageRectOutline from './ImageRectOutline.vue'
|
||||||
import ImageEllipseOutline from './ImageEllipseOutline.vue'
|
import ImageEllipseOutline from './ImageEllipseOutline.vue'
|
||||||
@ -113,6 +114,9 @@ export default defineComponent({
|
|||||||
const shadow = computed(() => props.elementInfo.shadow)
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
const { shadowStyle } = useElementShadow(shadow)
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
|
|
||||||
|
const flip = computed(() => props.elementInfo.flip)
|
||||||
|
const { flipStyle } = useElementFlip(flip)
|
||||||
|
|
||||||
const handleSelectElement = (e: MouseEvent) => {
|
const handleSelectElement = (e: MouseEvent) => {
|
||||||
if (props.elementInfo.lock) return
|
if (props.elementInfo.lock) return
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@ -159,15 +163,6 @@ export default defineComponent({
|
|||||||
return filter
|
return filter
|
||||||
})
|
})
|
||||||
|
|
||||||
const flip = computed(() => {
|
|
||||||
if (!props.elementInfo.flip) return ''
|
|
||||||
const { x, y } = props.elementInfo.flip
|
|
||||||
if (x && y) return `rotateX(${x}deg) rotateY(${y}deg)`
|
|
||||||
else if (x) return `rotateX(${x}deg)`
|
|
||||||
else if (y) return `rotateY(${y}deg)`
|
|
||||||
return ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const clip = (data: ImageClipedEmitData) => {
|
const clip = (data: ImageClipedEmitData) => {
|
||||||
store.commit(MutationTypes.SET_CLIPING_IMAGE_ELEMENT_ID, '')
|
store.commit(MutationTypes.SET_CLIPING_IMAGE_ELEMENT_ID, '')
|
||||||
|
|
||||||
@ -195,7 +190,7 @@ export default defineComponent({
|
|||||||
clipShape,
|
clipShape,
|
||||||
imgPosition,
|
imgPosition,
|
||||||
filter,
|
filter,
|
||||||
flip,
|
flipStyle,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
:style="{
|
:style="{
|
||||||
opacity: elementInfo.opacity,
|
opacity: elementInfo.opacity,
|
||||||
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||||
|
transform: flipStyle,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<SvgWrapper
|
<SvgWrapper
|
||||||
@ -55,6 +56,7 @@ import { computed, defineComponent, PropType } from 'vue'
|
|||||||
import { PPTShapeElement } from '@/types/slides'
|
import { PPTShapeElement } from '@/types/slides'
|
||||||
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
||||||
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||||
|
import useElementFlip from '@/views/components/element/hooks/useElementFlip'
|
||||||
|
|
||||||
import GradientDefs from './GradientDefs.vue'
|
import GradientDefs from './GradientDefs.vue'
|
||||||
|
|
||||||
@ -76,11 +78,15 @@ export default defineComponent({
|
|||||||
const shadow = computed(() => props.elementInfo.shadow)
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
const { shadowStyle } = useElementShadow(shadow)
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
|
|
||||||
|
const flip = computed(() => props.elementInfo.flip)
|
||||||
|
const { flipStyle } = useElementFlip(flip)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shadowStyle,
|
shadowStyle,
|
||||||
outlineWidth,
|
outlineWidth,
|
||||||
outlineStyle,
|
outlineStyle,
|
||||||
outlineColor,
|
outlineColor,
|
||||||
|
flipStyle,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
:style="{
|
:style="{
|
||||||
opacity: elementInfo.opacity,
|
opacity: elementInfo.opacity,
|
||||||
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||||
|
transform: flipStyle,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<SvgWrapper
|
<SvgWrapper
|
||||||
@ -59,6 +60,7 @@ import { PPTShapeElement } from '@/types/slides'
|
|||||||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||||
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
||||||
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||||
|
import useElementFlip from '@/views/components/element/hooks/useElementFlip'
|
||||||
|
|
||||||
import GradientDefs from './GradientDefs.vue'
|
import GradientDefs from './GradientDefs.vue'
|
||||||
|
|
||||||
@ -94,12 +96,16 @@ export default defineComponent({
|
|||||||
const shadow = computed(() => props.elementInfo.shadow)
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
const { shadowStyle } = useElementShadow(shadow)
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
|
|
||||||
|
const flip = computed(() => props.elementInfo.flip)
|
||||||
|
const { flipStyle } = useElementFlip(flip)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleSelectElement,
|
handleSelectElement,
|
||||||
shadowStyle,
|
shadowStyle,
|
||||||
outlineWidth,
|
outlineWidth,
|
||||||
outlineStyle,
|
outlineStyle,
|
||||||
outlineColor,
|
outlineColor,
|
||||||
|
flipStyle,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
21
src/views/components/element/hooks/useElementFlip.ts
Normal file
21
src/views/components/element/hooks/useElementFlip.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { ref, Ref, watchEffect } from 'vue'
|
||||||
|
import { ImageOrShapeFlip } from '@/types/slides'
|
||||||
|
|
||||||
|
export default (flip: Ref<ImageOrShapeFlip | undefined>) => {
|
||||||
|
const flipStyle = ref('')
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (flip.value) {
|
||||||
|
const { x, y } = flip.value
|
||||||
|
if (x && y) flipStyle.value = `rotateX(${x}deg) rotateY(${y}deg)`
|
||||||
|
else if (x) flipStyle.value = `rotateX(${x}deg)`
|
||||||
|
else if (y) flipStyle.value = `rotateY(${y}deg)`
|
||||||
|
else flipStyle.value = ''
|
||||||
|
}
|
||||||
|
else flipStyle.value = ''
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
flipStyle,
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user