feat: 全局主题设置支持边框、阴影相关属性

This commit is contained in:
pipipi-pikachu 2023-11-04 18:28:51 +08:00
parent 34488b6eb4
commit 99c53c385d
6 changed files with 130 additions and 7 deletions

View File

@ -129,9 +129,9 @@ export default () => {
} }
// 将当前主题配置应用到全部页面 // 将当前主题配置应用到全部页面
const applyThemeToAllSlides = () => { const applyThemeToAllSlides = (applyAll = false) => {
const newSlides: Slide[] = JSON.parse(JSON.stringify(slides.value)) const newSlides: Slide[] = JSON.parse(JSON.stringify(slides.value))
const { themeColor, backgroundColor, fontColor, fontName } = theme.value const { themeColor, backgroundColor, fontColor, fontName, outline, shadow } = theme.value
for (const slide of newSlides) { for (const slide of newSlides) {
if (!slide.background || slide.background.type !== 'image') { if (!slide.background || slide.background.type !== 'image') {
@ -142,6 +142,11 @@ export default () => {
} }
for (const el of slide.elements) { for (const el of slide.elements) {
if (applyAll) {
if ('outline' in el && el.outline) el.outline = outline
if ('shadow' in el && el.shadow) el.shadow = shadow
}
if (el.type === 'shape') el.fill = themeColor if (el.type === 'shape') el.fill = themeColor
else if (el.type === 'line') el.color = themeColor else if (el.type === 'line') el.color = themeColor
else if (el.type === 'text') { else if (el.type === 'text') {

View File

@ -5,4 +5,15 @@ export const theme: SlideTheme = {
fontColor: '#333', fontColor: '#333',
fontName: 'Microsoft Yahei', fontName: 'Microsoft Yahei',
backgroundColor: '#fff', backgroundColor: '#fff',
shadow: {
h: 3,
v: 3,
blur: 2,
color: '#808080',
},
outline: {
width: 2,
color: '#525252',
style: 'solid',
},
} }

View File

@ -694,4 +694,6 @@ export interface SlideTheme {
themeColor: string themeColor: string
fontColor: string fontColor: string
fontName: string fontName: string
outline: PPTElementOutline
shadow: PPTElementShadow
} }

View File

@ -119,7 +119,14 @@
<Divider /> <Divider />
<div class="title">全局主题</div> <div class="title">
<span>全局主题</span>
<span class="more" @click="moreThemeConfigsVisible = !moreThemeConfigsVisible">
<span class="text">更多</span>
<IconDown v-if="moreThemeConfigsVisible" />
<IconRight v-else />
</span>
</div>
<div class="row"> <div class="row">
<div style="width: 40%;">字体</div> <div style="width: 40%;">字体</div>
<Select <Select
@ -169,8 +176,88 @@
</Popover> </Popover>
</div> </div>
<template v-if="moreThemeConfigsVisible">
<div class="row"> <div class="row">
<Button style="flex: 1;" @click="applyThemeToAllSlides()">应用主题到全部</Button> <div style="width: 40%;">边框样式</div>
<Select
style="width: 60%;"
:value="theme.outline.style || ''"
@update:value="value => updateTheme({ outline: { ...theme.outline, style: value as 'dashed' | 'solid' } })"
:options="[
{ label: '实线边框', value: 'solid' },
{ label: '虚线边框', value: 'dashed' },
]"
/>
</div>
<div class="row">
<div style="width: 40%;">边框颜色</div>
<Popover trigger="click" style="width: 60%;">
<template #content>
<ColorPicker
:modelValue="theme.outline.color"
@update:modelValue="value => updateTheme({ outline: { ...theme.outline, color: value } })"
/>
</template>
<ColorButton :color="theme.outline.color || '#000'" />
</Popover>
</div>
<div class="row">
<div style="width: 40%;">边框粗细</div>
<NumberInput
:value="theme.outline.width || 0"
@update:value="value => updateTheme({ outline: { ...theme.outline, width: value } })"
style="width: 60%;"
/>
</div>
<div class="row" style="height: 30px;">
<div style="width: 40%;">水平阴影</div>
<Slider
style="width: 60%;"
:min="-10"
:max="10"
:step="1"
:value="theme.shadow.h"
@update:value="value => updateTheme({ shadow: { ...theme.shadow, h: value as number } })"
/>
</div>
<div class="row" style="height: 30px;">
<div style="width: 40%;">垂直阴影</div>
<Slider
style="width: 60%;"
:min="-10"
:max="10"
:step="1"
:value="theme.shadow.v"
@update:value="value => updateTheme({ shadow: { ...theme.shadow, v: value as number } })"
/>
</div>
<div class="row" style="height: 30px;">
<div style="width: 40%;">模糊距离</div>
<Slider
style="width: 60%;"
:min="1"
:max="20"
:step="1"
:value="theme.shadow.blur"
@update:value="value => updateTheme({ shadow: { ...theme.shadow, blur: value as number } })"
/>
</div>
<div class="row">
<div style="width: 40%;">阴影颜色</div>
<Popover trigger="click" style="width: 60%;">
<template #content>
<ColorPicker
:modelValue="theme.shadow.color"
@update:modelValue="value => updateTheme({ shadow: { ...theme.shadow, color: value } })"
/>
</template>
<ColorButton :color="theme.shadow.color" />
</Popover>
</div>
</template>
<div class="row">
<Button style="flex: 1;" @click="applyThemeToAllSlides(moreThemeConfigsVisible)">应用主题到全部</Button>
</div> </div>
<Divider /> <Divider />
@ -203,7 +290,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed, ref } from 'vue'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore } from '@/store' import { useMainStore, useSlidesStore } from '@/store'
import type { SlideBackground, SlideTheme } from '@/types/slides' import type { SlideBackground, SlideTheme } from '@/types/slides'
@ -221,11 +308,14 @@ import Slider from '@/components/Slider.vue'
import Button from '@/components/Button.vue' import Button from '@/components/Button.vue'
import Select from '@/components/Select.vue' import Select from '@/components/Select.vue'
import Popover from '@/components/Popover.vue' import Popover from '@/components/Popover.vue'
import NumberInput from '@/components/NumberInput.vue'
const slidesStore = useSlidesStore() const slidesStore = useSlidesStore()
const { availableFonts } = storeToRefs(useMainStore()) const { availableFonts } = storeToRefs(useMainStore())
const { slides, currentSlide, viewportRatio, theme } = storeToRefs(slidesStore) const { slides, currentSlide, viewportRatio, theme } = storeToRefs(slidesStore)
const moreThemeConfigsVisible = ref(false)
const background = computed(() => { const background = computed(() => {
if (!currentSlide.value.background) { if (!currentSlide.value.background) {
return { return {
@ -322,7 +412,18 @@ const updateViewportRatio = (value: number) => {
margin-bottom: 10px; margin-bottom: 10px;
} }
.title { .title {
display: flex;
justify-content: space-between;
margin-bottom: 10px; margin-bottom: 10px;
.more {
cursor: pointer;
.text {
font-size: 12px;
margin-right: 3px;
}
}
} }
.background-image-wrapper { .background-image-wrapper {
margin-bottom: 10px; margin-bottom: 10px;

View File

@ -67,6 +67,7 @@ withDefaults(defineProps<{
}) })
const slidesStore = useSlidesStore() const slidesStore = useSlidesStore()
const { theme } = storeToRefs(slidesStore)
const { handleElement } = storeToRefs(useMainStore()) const { handleElement } = storeToRefs(useMainStore())
const outline = ref<PPTElementOutline>() const outline = ref<PPTElementOutline>()
@ -90,7 +91,7 @@ const updateOutline = (outlineProps: Partial<PPTElementOutline>) => {
const toggleOutline = (checked: boolean) => { const toggleOutline = (checked: boolean) => {
if (!handleElement.value) return if (!handleElement.value) return
if (checked) { if (checked) {
const _outline: PPTElementOutline = { width: 2, color: '#000', style: 'solid' } const _outline: PPTElementOutline = theme.value.outline
slidesStore.updateElement({ id: handleElement.value.id, props: { outline: _outline } }) slidesStore.updateElement({ id: handleElement.value.id, props: { outline: _outline } })
} }
else { else {
@ -103,6 +104,7 @@ const toggleOutline = (checked: boolean) => {
<style lang="scss" scoped> <style lang="scss" scoped>
.row { .row {
width: 100%; width: 100%;
height: 30px;
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 10px; margin-bottom: 10px;

View File

@ -70,6 +70,7 @@ import Slider from '@/components/Slider.vue'
import Popover from '@/components/Popover.vue' import Popover from '@/components/Popover.vue'
const slidesStore = useSlidesStore() const slidesStore = useSlidesStore()
const { theme } = storeToRefs(slidesStore)
const { handleElement } = storeToRefs(useMainStore()) const { handleElement } = storeToRefs(useMainStore())
const shadow = ref<PPTElementShadow>() const shadow = ref<PPTElementShadow>()
@ -93,7 +94,7 @@ const updateShadow = (shadowProps: Partial<PPTElementShadow>) => {
const toggleShadow = (checked: boolean) => { const toggleShadow = (checked: boolean) => {
if (!handleElement.value) return if (!handleElement.value) return
if (checked) { if (checked) {
const _shadow: PPTElementShadow = { h: 1, v: 1, blur: 2, color: '#000' } const _shadow: PPTElementShadow = theme.value.shadow
slidesStore.updateElement({ id: handleElement.value.id, props: { shadow: _shadow } }) slidesStore.updateElement({ id: handleElement.value.id, props: { shadow: _shadow } })
} }
else { else {
@ -106,6 +107,7 @@ const toggleShadow = (checked: boolean) => {
<style lang="scss" scoped> <style lang="scss" scoped>
.row { .row {
width: 100%; width: 100%;
height: 30px;
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 10px; margin-bottom: 10px;